diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index 6a84d21..0000000
--- a/.dockerignore
+++ /dev/null
@@ -1,13 +0,0 @@
-# Ignore build files
-build/
-cmake-build-*
-
-# Ignore editor-specific files
-*.swp
-*.log
-.idea/
-.vscode/
-
-# Ignore version control directories
-.git/
-.gitignore
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 91d3764..04337c1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.16)
+cmake_policy(SET CMP0167 OLD)
project(short-link VERSION 1.0 LANGUAGES CXX)
@@ -8,23 +9,13 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
set(BUILD_SHARED_LIBS OFF)
include_directories(${PROJECT_SOURCE_DIR}/includes)
-
-add_executable(Application
- src/main.cpp
- src/http_connection.cpp
- src/http_server.cpp
- src/request_handler.cpp
- src/database_connection.cpp
+add_executable(Application src/main.cpp
+ src/http_connection.cpp
+ src/http_server.cpp
+ src/request_handler.cpp
)
-add_custom_target(copy_frontend ALL
- COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/frontend ${CMAKE_BINARY_DIR}/frontend
-)
-
-add_dependencies(Application copy_frontend)
-
-
-find_package(Boost REQUIRED COMPONENTS filesystem system)
+find_package(Boost REQUIRED filesystem system)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
@@ -32,22 +23,4 @@ if(Boost_FOUND)
target_link_libraries(Application PRIVATE Boost::filesystem Boost::system)
else()
message(FATAL_ERROR "Boost not found!")
-endif()
-
-find_package(SOCI REQUIRED sqlite3)
-
-if(SOCI_FOUND)
- include_directories(${SOCI_INCLUDE_DIRS})
- target_link_libraries(Application PRIVATE soci_core soci_sqlite3)
-else()
- message(FATAL_ERROR "SOCI not found!")
-endif()
-
-find_package(SQLite3 REQUIRED)
-
-if(SQLite3_FOUND)
- include_directories(${SQLite3_INCLUDE_DIRS})
- target_link_libraries(Application PRIVATE SQLite::SQLite3)
-else()
- message(FATAL_ERROR "SQLite3 not found!")
-endif()
+endif()
\ No newline at end of file
diff --git a/dockerfile b/dockerfile
index 327ebbc..ffc27dd 100644
--- a/dockerfile
+++ b/dockerfile
@@ -3,24 +3,12 @@ FROM ubuntu:20.04
ENV APPLICATION_BINARY="Application"
ENV OPEN_PORT=8080
+COPY ${APPLICATION_BINARY} .
+
RUN apt-get update && \
ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime && \
- DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
- build-essential \
- cmake \
- libboost-all-dev \
- git \
- libsqlite3-dev && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends libboost-all-dev && \
rm -rf /var/lib/apt/lists/*
-WORKDIR /app
-
-COPY . .
-
-RUN mkdir build && cd build && \
- cmake .. && \
- make
-
EXPOSE ${OPEN_PORT}
-
-CMD ["./build/Application"]
+CMD [ "/${APPLICATION_BINARY}" ]
\ No newline at end of file
diff --git a/frontend/index.html b/frontend/index.html
deleted file mode 100644
index 673756a..0000000
--- a/frontend/index.html
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
- Short-Link
-
-
-
-
-
-
Short-Link
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/frontend/index.js b/frontend/index.js
deleted file mode 100644
index da7e0ed..0000000
--- a/frontend/index.js
+++ /dev/null
@@ -1,43 +0,0 @@
-document.getElementById("urlInput").addEventListener("input", function() {
- const urlInput = document.getElementById("urlInput");
- const shortenButton = document.getElementById("shortenButton");
-
- const urlPattern = /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/i
-
- const isValid = urlPattern.test(urlInput.value);
-
- if (isValid) {
- urlInput.style.borderColor = "green";
- shortenButton.disabled = false;
- } else {
- urlInput.style.borderColor = "red";
- shortenButton.disabled = true;
- }
-});
-
-document.getElementById("shortenButton").addEventListener("click", async function() {
- const urlInput = document.getElementById("urlInput").value;
- const outputContainer = document.getElementById("output-container");
- const errorContainer = document.getElementById("error-container");
-
- if (urlInput) {
- // const shortenedUrl = urlInput + "-short"; // only for testing
-
- const response = await fetch("/", {
- method: "POST",
- headers: {
- "Content-Type": "text/plain",
- },
- body: urlInput,
- });
- if (response.ok){
- const shortenedUrl = await response.text();
- outputContainer.innerHTML = `Shortened URL: ${shortenedUrl}
`;
- errorContainer.innerHTML = "";
- }else{
- const error = await response.text();
- errorContainer.innerHTML = `Something went wrong: ${error}
`;
- outputContainer.innerHTML = "";
- }
- }
-});
diff --git a/includes/database_service.hpp b/includes/database_service.hpp
index bb5cb52..4d997e3 100644
--- a/includes/database_service.hpp
+++ b/includes/database_service.hpp
@@ -1,35 +1,28 @@
#ifndef DATABASE_SERVICE_HPP
#define DATABASE_SERVICE_HPP
-#include
-#include
-#include
+#include
+#include
+#include
#include
-#include
-#include
class DatabaseService {
public:
- static DatabaseService& getInstance(const std::string& db_path, std::size_t pool_size);
- void shortenURL(const std::string& longURL, std::function callback);
- void getLongURL(const std::string& shortCode, std::function callback);
+ static DatabaseService& getInstance(boost::asio::io_context& io_context, std::size_t pool_size);
+
private:
- DatabaseService(const std::string& db_path, std::size_t pool_size = 4);
+ std::shared_ptr getConnection(); //retrive a connection from the connection pool
+
+ DatabaseService(boost::asio::io_context& io_context, std::size_t pool_size = 4);
~DatabaseService();
DatabaseService(DatabaseService const&);
void operator=(DatabaseService const&);
- std::shared_ptr getConnection();
- std::string generateShortUUID();
-
- std::string db_path_;
+ boost::asio::io_context& io_context_;
std::size_t pool_size_;
- std::vector> connection_pool_;
- std::size_t current_index_ = 0;
-
- static DatabaseService* INSTANCE;
- static std::mutex singleton_mutex;
+ std::vector> connection_pool_;
+ std::size_t current_index_ = 0; //index used for round robin selection
};
#endif
\ No newline at end of file
diff --git a/includes/http_connection.hpp b/includes/http_connection.hpp
index 493c11c..ab53345 100644
--- a/includes/http_connection.hpp
+++ b/includes/http_connection.hpp
@@ -8,7 +8,6 @@
#include
#include
#include
-#include
using namespace std;
using namespace boost::asio;
@@ -26,7 +25,7 @@ private:
ip::tcp::socket socket_;
boost::beast::flat_buffer buffer_;
http::request request_;
- variant, http::response> response_;
+ http::response response_;
RequestHandler request_handler_;
void read();
diff --git a/includes/log_utils.hpp b/includes/log_utils.hpp
deleted file mode 100644
index 8d44567..0000000
--- a/includes/log_utils.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef LOG_UTILS_HPP
-#define LOG_UTILS_HPP
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-// Define log levels
-enum class LogLevel {
- INFO,
- ERROR,
- BADREQUEST
-};
-
-// Function to get a color based on file name
-inline const char* getColorForFile(const std::string& file_name) {
- // Define color codes
- const char* colors[] = {
- "\033[34m", // Blue
- "\033[32m", // Green
- "\033[36m", // Cyan
- "\033[35m", // Magenta
- "\033[33m" // Yellow
- };
-
- // Hash the file name to determine the color
- std::hash hasher;
- size_t hash = hasher(file_name);
- return colors[hash % (sizeof(colors) / sizeof(colors[0]))];
-}
-
-// Logging function
-inline void log(const LogLevel level, const std::string& message, const std::string& file_path) {
- const auto now = std::chrono::system_clock::now();
- const auto time = std::chrono::system_clock::to_time_t(now);
- const std::tm tm = *std::localtime(&time);
-
- const auto color_reset = "\033[0m";
- const auto color_error = "\033[31m";
-
- // Extract file name from full path
- std::string file_name = std::filesystem::path(file_path).filename().string();
-
- // Determine color based on log level
- const char* color = level == LogLevel::ERROR || level == LogLevel::BADREQUEST ? color_error : getColorForFile(file_name);
-
- // Print log
- std::cout << color
- << "[" << (level == LogLevel::INFO ? "INFO" : "ERROR") << "] "
- << std::put_time(&tm, "%H:%M:%S") // Time only
- << " [" << file_name << "] "
- << message
- << color_reset << std::endl;
-}
-
-// Helper macro to automatically pass the file name
-#define LOG(level, message) log(level, message, __FILE__)
-
-#endif // LOG_UTILS_HPP
diff --git a/includes/request_handler.hpp b/includes/request_handler.hpp
index 5382f6f..2d14176 100644
--- a/includes/request_handler.hpp
+++ b/includes/request_handler.hpp
@@ -4,20 +4,14 @@
#include
#include
#include
-#include
using namespace boost::beast;
class RequestHandler {
public:
-std::variant<
- http::response,
- http::response
- > handle(const http::request& request);
+ http::response handle(const http::request& request);
private:
- http::response BadRequest(const std::string& why);
- http::response handle_file_request(const std::string& filename, boost::system::error_code& ec);
};
#endif
\ No newline at end of file
diff --git a/src/database_connection.cpp b/src/database_connection.cpp
index 98f2db5..194bf2b 100644
--- a/src/database_connection.cpp
+++ b/src/database_connection.cpp
@@ -1,111 +1,16 @@
#include "../includes/database_service.hpp"
-#include "../includes/log_utils.hpp"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include
-std::mutex DatabaseService::singleton_mutex;
-DatabaseService* DatabaseService::INSTANCE = nullptr;
-DatabaseService& DatabaseService::getInstance(const std::string& db_path, std::size_t pool_size) {
-
- std::lock_guard lock(singleton_mutex);
- if (!INSTANCE) {
- INSTANCE = new DatabaseService(db_path, pool_size);
- }
- return *INSTANCE;
+DatabaseService& DatabaseService::getInstance(boost::asio::io_context &io_context, std::size_t pool_size) {
+ static DatabaseService INSTANCE;
+ return INSTANCE;
}
-DatabaseService::DatabaseService(const std::string& db_path, std::size_t pool_size)
- : db_path_(db_path), pool_size_(pool_size), current_index_(0) {
- LOG(LogLevel::INFO, "Initializing DatabaseService");
+DatabaseService::DatabaseService(boost::asio::io_context& io_context, std::size_t pool_size) : io_context_(io_context), pool_size_(pool_size) {
- if (pool_size_ <= 0) {
- LOG(LogLevel::ERROR, "Invalid pool size");
- throw std::invalid_argument("Connection pool size must be greater than zero.");
- }
-
- for (std::size_t i = 0; i < pool_size_; ++i) {
- auto session = std::make_shared(soci::sqlite3, db_path_);
- connection_pool_.emplace_back(session);
- }
- LOG(LogLevel::INFO, "Connection Pool filled");
- try {
- auto conn = getConnection();
- *conn << "CREATE TABLE IF NOT EXISTS urls ("
- "short_code CHAR(8) PRIMARY KEY, "
- "original_url TEXT NOT NULL UNIQUE, "
- "created_at INTEGER DEFAULT CURRENT_TIMESTAMP);";
-
- LOG(LogLevel::INFO, "Database schema initialized successfully");
- } catch (const std::exception& e) {
- LOG(LogLevel::ERROR, "Exception during schema creation: " + std::string(e.what()));
- throw;
- }
}
DatabaseService::~DatabaseService() {
- connection_pool_.clear();
-}
-std::shared_ptr DatabaseService::getConnection() {
- if (connection_pool_.empty()) {
- throw std::runtime_error("Connection pool is empty.");
- }
-
- LOG(LogLevel::INFO, "Returning connection at index: " + std::to_string(current_index_));
- auto conn = connection_pool_[current_index_];
- current_index_ = (current_index_ + 1) % connection_pool_.size();
- return conn;
-}
-
-std::string DatabaseService::generateShortUUID() {
- boost::uuids::uuid uuid = boost::uuids::random_generator()();
- std::string uuidStr = to_string(uuid);
- return uuidStr.substr(0, 8);
-}
-
-void DatabaseService::shortenURL(const std::string& longURL, std::function callback) {
- LOG(LogLevel::INFO, "Shortening URL: " + longURL);
- try {
- auto conn = getConnection();
- std::string shortCode;
- *conn << "SELECT short_code FROM urls WHERE original_url = :long_url", soci::use(longURL), soci::into(shortCode);
-
- if (!shortCode.empty()) {
- callback({}, shortCode);
- return;
- }
-
- shortCode = generateShortUUID();
- *conn << "INSERT INTO urls (short_code, original_url) VALUES (:short_code, :long_url)",
- soci::use(shortCode), soci::use(longURL);
- callback({}, shortCode);
- } catch (const std::exception& e) {
- LOG(LogLevel::ERROR, "Failed to shorten URL: " + std::string(e.what()));
- callback(std::make_error_code(std::errc::io_error), "");
- }
-}
-
-void DatabaseService::getLongURL(const std::string& shortCode, std::function callback) {
- LOG(LogLevel::INFO, "Getting long-URL from short-code: " + shortCode);
- try {
- auto conn = getConnection();
- std::string longURL;
- *conn << "SELECT original_url FROM urls WHERE short_code = :short_code", soci::use(shortCode), soci::into(longURL);
-
- if (!longURL.empty()) {
- callback({}, longURL);
- } else {
- LOG(LogLevel::ERROR, "Short code not found: " + shortCode);
- callback(std::make_error_code(std::errc::no_such_file_or_directory), "");
- }
- } catch (const std::exception& e) {
- LOG(LogLevel::ERROR, "Failed to retrieve long URL: " + std::string(e.what()));
- callback(std::make_error_code(std::errc::io_error), "");
- }
}
\ No newline at end of file
diff --git a/src/http_connection.cpp b/src/http_connection.cpp
index 949cd52..7741622 100644
--- a/src/http_connection.cpp
+++ b/src/http_connection.cpp
@@ -1,8 +1,9 @@
#include "../includes/http_connection.hpp"
-#include "../includes/log_utils.hpp"
+#include
#include
#include
#include
+#include
HttpConnection::HttpConnection(io_context& io_context)
: socket_(io_context) {};
@@ -21,35 +22,23 @@ void HttpConnection::process_connection() {
void HttpConnection::read() {
auto self = shared_from_this();
-
http::async_read(socket_, buffer_, request_,
[self](boost::beast::error_code error_code, size_t bytes_transferred) {
if (!error_code) {
- LOG(LogLevel::INFO, "Request details:");
- LOG(LogLevel::INFO, "\tMethod: " + std::string(self->request_.method_string()));
- LOG(LogLevel::INFO, "\tTarget: " + std::string(self->request_.target()));
- LOG(LogLevel::INFO, "\tBody: " + std::string(self->request_.body()));
-
- // Log before forwarding to RequestHandler
- LOG(LogLevel::INFO, "Forwarding request to RequestHandler...");
+ cout << "Request received:\n" << self->request_ << endl;
self->response_ = self->request_handler_.handle(self->request_);
-
self->write();
} else {
- LOG(LogLevel::ERROR, "Error reading request: " + error_code.message());
+ cerr << "Error reading: " << error_code.message() << endl;
}
});
}
-
-
-
void HttpConnection::write() {
auto self = shared_from_this();
- std::visit(
- [this, self](auto& response) {
- http::async_write(socket_, response,
+ http::async_write(socket_, response_,
[self](boost::beast::error_code error_code, size_t bytes_transferred) {
+ cout << "Sending response:\n" << self->response_ << endl;
if (!error_code) {
auto error_code_socket = self->socket_.shutdown(ip::tcp::socket::shutdown_send, error_code);
if (error_code_socket) {
@@ -63,6 +52,4 @@ void HttpConnection::write() {
}
}
});
-
- }, response_);
}
\ No newline at end of file
diff --git a/src/http_server.cpp b/src/http_server.cpp
index 8f5c1d5..60b18c0 100644
--- a/src/http_server.cpp
+++ b/src/http_server.cpp
@@ -1,8 +1,8 @@
#include "../includes/http_server.hpp"
-#include "../includes/log_utils.hpp"
-#include
#include "http_connection.hpp"
+#include
#include
+#include
HttpServer::HttpServer(io_context& io_context, const ip::tcp::endpoint& endpoint)
: io_context_(io_context), acceptor_(io_context, endpoint) {
@@ -11,18 +11,14 @@ HttpServer::HttpServer(io_context& io_context, const ip::tcp::endpoint& endpoint
void HttpServer::accept_connection() {
HttpConnection::pointer new_connection = HttpConnection::create(io_context_);
-
- LOG(LogLevel::INFO, "Waiting for new connection");
- acceptor_.async_accept(new_connection->socket(),
- bind(&HttpServer::handle_accept, this, new_connection, boost::asio::placeholders::error));
+ acceptor_.async_accept(new_connection->socket(),
+ bind(&HttpServer::handle_accept, this, new_connection, boost::asio::placeholders::error)
+ );
}
void HttpServer::handle_accept(HttpConnection::pointer& new_connection, const boost::system::error_code& error_code) {
if (!error_code) {
- LOG(LogLevel::INFO, "Accepted new connection from: " + new_connection->socket().remote_endpoint().address().to_string());
new_connection->process_connection();
- } else {
- LOG(LogLevel::ERROR, "Error accepting connection: " + error_code.message());
}
accept_connection();
-}
+}
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 8b57e85..f7624fa 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,26 +1,24 @@
-#include "../includes/http_server.hpp"
-#include "../includes/log_utils.hpp"
-#include
-#include
+#include "../includes/http_server.hpp"
+#include
using namespace boost::asio;
-int main(int argc, char* argv[]) {
+int main(int argc, char *argv[]) {
const int port = 8080;
- LOG(LogLevel::INFO, "Starting Server on 127.0.0.1:" + std::to_string(port));
-
- try {
+ std::cout << "Starting server!" << std::endl;
+ try
+ {
io_context io_context;
ip::tcp::endpoint endpoint(ip::tcp::v4(), port);
-
HttpServer server(io_context, endpoint);
-
io_context.run();
- } catch (const std::exception& e) {
- LOG(LogLevel::ERROR, "Exception occurred: " + std::string(e.what()));
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
}
return 0;
-}
+}
\ No newline at end of file
diff --git a/src/request_handler.cpp b/src/request_handler.cpp
index 2116be2..f137959 100644
--- a/src/request_handler.cpp
+++ b/src/request_handler.cpp
@@ -1,173 +1,82 @@
#include "../includes/request_handler.hpp"
-#include "../includes/database_service.hpp"
-#include "../includes/log_utils.hpp"
#include
#include
#include
#include
#include
#include
-#include
-#include
-http::response RequestHandler::BadRequest(const std::string& why) {
- LOG(LogLevel::BADREQUEST, "BadRequest: " + why);
+http::response BadRequest(const std::string& why) {
http::response response;
response.result(http::status::bad_request);
response.set(http::field::server, "Beast");
response.set(http::field::content_type, "text/html");
response.body() = why;
- response.keep_alive(false);
response.prepare_payload();
return response;
}
-std::variant<
- http::response,
- http::response
-> RequestHandler::handle(const http::request& request) {
- std::string target = request.target();
+http::response RequestHandler::handle(const http::request& request) {
+ string_view target = request.target();
http::verb method = request.method();
-
- auto& dbService = DatabaseService::getInstance("urls.db", 4);
-
- LOG(LogLevel::INFO, "Received request:");
- LOG(LogLevel::INFO, "\tMethod: " + std::string(request.method_string()));
- LOG(LogLevel::INFO, "\tTarget: " + target);
- LOG(LogLevel::INFO, "\tBody: " + request.body());
-
- if (method == http::verb::post) {
- if (target != "/") {
- return BadRequest("Cannot post to anything other than /");
- }
- if (request.find(http::field::content_type) == request.end()) {
- return BadRequest("Content-Type header is required for POST requests");
- }
- auto content_type = request[http::field::content_type];
- if (content_type != "text/plain") {
- return BadRequest("Content-Type must be text/plain");
- }
-
- std::string url = request.body();
- std::regex url_regex("^(https?://)?(?:www\\.)?[-a-zA-Z0-9@%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&/=]*)$");
- std::smatch url_match;
- if (!std::regex_match(url, url_match, url_regex)) {
- return BadRequest("Invalid URL");
- }
- if (!url_match[1].matched) {
- url = "https://" + url;
- }
-
- LOG(LogLevel::INFO, "Valid URL: " + url);
-
- std::promise promise;
- std::future future = promise.get_future();
- dbService.shortenURL(url, [&promise](std::error_code ec, const std::string& shortURL) {
- if (!ec) {
- promise.set_value(shortURL);
- } else {
- promise.set_exception(std::make_exception_ptr(
- std::runtime_error(ec.message())
- ));
- }
- });
-
- try {
- std::string shortURL = future.get();
- LOG(LogLevel::INFO, "Shortened URL generated: " + shortURL);
+ if (target == "/") {
+ if(method == http::verb::get) {
+ //case 1: "/" -> serve angular frontend or static frontend what ever
http::response response;
- response.result(http::status::created);
- response.set(http::field::server, "Beast");
- response.set(http::field::content_type, "text/plain");
- response.body() = "127.0.0.1:8080/" + shortURL;
- response.keep_alive(false);
- response.prepare_payload();
- return response;
- } catch (const std::exception& e) {
- return BadRequest("Error generating short URL: " + std::string(e.what()));
- }
-
- }
- if (method == http::verb::get) {
- if (target == "/") {
- LOG(LogLevel::INFO, "Serving the index.html file for target /");
- target = "/index.html";
- }
- if (target == "/index.html" || target == "/index.js") {
- error_code ec;
- http::response response = handle_file_request("frontend" + target, ec);
- if (ec) {
- LOG(LogLevel::ERROR, "Failed to read file: frontend" + target);
- return BadRequest("Error reading file");
- } else {
- LOG(LogLevel::INFO, "Served file: frontend" + target);
- return response;
- }
- }
-
- std::regex regex_pattern(".*/([^/]+)$");
- std::smatch match;
- std::string shortCode;
-
- if (std::regex_match(target, match, regex_pattern)) {
- shortCode = match[1];
- LOG(LogLevel::INFO, "Extracted short code using regex: " + shortCode);
- } else {
- LOG(LogLevel::ERROR, "Failed to extract short code from target: " + target);
- return BadRequest("Invalid short URL format");
- }
-
- std::promise promise;
- std::future future = promise.get_future();
-
- dbService.getLongURL(shortCode, [&promise](std::error_code ec, const std::string& longURL) {
- if (!ec) {
- promise.set_value(longURL);
- } else {
- promise.set_exception(std::make_exception_ptr(
- std::runtime_error(ec.message())
- ));
- }
- });
-
- try {
- std::string expandedURL = future.get();
- LOG(LogLevel::INFO, "Expanded URL: " + expandedURL);
- http::response response;
- response.result(http::status::moved_permanently);
- response.set(http::field::location, expandedURL);
+ response.result(http::status::ok);
response.version(request.version());
response.set(http::field::server, "Beast");
- response.body() = "Redirecting to " + expandedURL;
- response.keep_alive(false);
+ response.set(http::field::content_type, "text/html");
+
+ //todo: load angular application / plain html & js
+ response.body() = "TEST
";
+
response.prepare_payload();
return response;
- } catch (const std::exception& e) {
- LOG(LogLevel::ERROR, "Failed to expand short URL: " + shortCode + ". Error: " + std::string(e.what()));
- return BadRequest("Short URL not found or error: " + std::string(e.what()));
+ }
+ else if (method == http::verb::post) {
+ if(request.find(http::field::content_type) == request.end()) {
+ return BadRequest("Content-Type header is required for POST requests");
+ }
+ auto content_type = request[http::field::content_type];
+ if(content_type != "text/plain") {
+ return BadRequest("Content-Type must be text/plain");
+ }
+ std::string url = request.body();
+ std::regex url_regex("^(https?://)?(?:www\\.)?[-a-zA-Z0-9@%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&/=]*)$");
+ std::smatch url_match;
+ if(!std::regex_match(url, url_match, url_regex)) {
+ return BadRequest("Invalid URL");
+ }
+ if(!url_match[1].matched){
+ url = "https://" + url;
+ }
+
+ //todo: save url to database and return short url
+
+ return BadRequest("Request is actually not bad. processing " + url);
+ }
+ }else {
+ if(method == http::verb::get){
+ http::response response;
+ std::string short_url = target.substr(1);
+
+ std::string expanded_url = "https://google.com"; //todo: get expanded url from database
+
+ response.result(http::status::moved_permanently);
+ response.set(http::field::location, expanded_url);
+ response.version(request.version());
+ response.set(http::field::server, "Beast");
+ response.body() = "Redirecting to " + expanded_url;
+ response.prepare_payload();
+ return response;
+ } else {
+ return BadRequest("Method not allowed");
}
}
+
+ //case 2: "/url" -> redirect to expanded url
+ //case 3: neither -> redirect to 404
return BadRequest("No rule matched.");
-}
-
-http::response RequestHandler::handle_file_request(const std::string& path, error_code& ec) {
- http::file_body::value_type file;
- file.open(path.c_str(), file_mode::read, ec);
- if (!ec) {
- LOG(LogLevel::INFO, "Successfully opened file: " + path);
- } else {
- LOG(LogLevel::ERROR, "Failed to open file: " + path + ". Error: " + ec.message());
- }
-
- http::response response;
- if (!ec) {
- response.result(http::status::ok);
- response.set(http::field::server, "Beast");
- response.set(http::field::content_type, "text/html");
- response.body() = std::move(file);
- response.keep_alive(false);
- response.prepare_payload();
- }
- return response;
}
\ No newline at end of file