
Webserv
HTTP Web Server Implementation
The Webserv project at 42 Abu Dhabi involved creating a fully functioning HTTP web server from scratch using C++. This project required deep understanding of how web servers work, including handling HTTP requests, serving static files, and generating dynamic content. One of the key learning outcomes was mastering the concept of file descriptor (fd) queues for managing multiple client connections simultaneously. Additionally, we delved into the intricacies of HTTP protocols, including GET and POST methods, as well as parsing and managing configuration files for server behavior. This project not only solidified my knowledge of network programming but also provided valuable experience in building scalable and efficient server-side applications.
Key Features
HTTP Request Handling
Complete implementation of HTTP GET and POST methods with proper request parsing, validation, and response generation following HTTP/1.1 standards.
File Descriptor Queues
Sophisticated fd queue management system for handling multiple simultaneous client connections efficiently without blocking operations.
Static File Serving
Efficient static file serving capabilities with MIME type detection and proper content delivery for various file formats.
Configuration Management
Flexible configuration file parsing system allowing customization of server behavior, ports, routes, and various settings.
Multi-Client Support
Concurrent handling of multiple client connections with efficient resource management and scalable architecture design.
Error Handling
Robust error handling system with proper HTTP status codes, error pages, and graceful failure management.
Development Journey
Architecture Design
Designed the server architecture, defined the HTTP protocol implementation strategy, and established the file descriptor management system.
Core Server Implementation
Implemented the basic HTTP server functionality with socket programming, request parsing, and response generation capabilities.
Advanced Features
Added file descriptor queues for multi-client support, configuration file parsing, and static file serving capabilities.
Testing & Optimization
Conducted comprehensive testing, performance optimization, error handling improvements, and scalability enhancements.
Challenges & Solutions
File Descriptor Management
Implementing efficient fd queues for handling multiple simultaneous client connections without blocking operations or resource leaks.
Developed a sophisticated queue system that manages file descriptors efficiently, allowing the server to handle multiple clients concurrently with proper resource cleanup.
HTTP Protocol Implementation
Understanding and correctly implementing HTTP/1.1 protocol specifications from scratch with proper request parsing and response generation.
Deep study of HTTP specifications and careful implementation of request parsing, response generation, and protocol compliance with comprehensive testing.
Configuration System
Creating a flexible configuration system that allows customization of server behavior while maintaining simplicity and robustness.
Built a robust parser that handles various configuration options for server ports, routes, and behavior settings with proper error handling.
// HTTP request parsing implementation
class HTTPRequest {
std::string method;
std::string uri;
std::string version;
std::map<std::string, std::string> headers;
std::string body;
public:
bool parseRequest(const std::string& rawRequest) {
std::istringstream stream(rawRequest);
std::string line;
// Parse request line
if (std::getline(stream, line)) {
std::istringstream lineStream(line);
lineStream >> method >> uri >> version;
}
// Parse headers
while (std::getline(stream, line) && !line.empty()) {
size_t colonPos = line.find(':');
if (colonPos != std::string::npos) {
std::string key = line.substr(0, colonPos);
std::string value = line.substr(colonPos + 2);
headers[key] = value;
}
}
return !method.empty() && !uri.empty();
}
};
// File descriptor queue for managing connections
class FDQueue {
std::queue<int> availableFDs;
std::map<int, ClientConnection> activeConnections;
public:
int getNextFD() {
if (!availableFDs.empty()) {
int fd = availableFDs.front();
availableFDs.pop();
return fd;
}
return -1;
}
void releaseFD(int fd) {
if (activeConnections.find(fd) != activeConnections.end()) {
activeConnections.erase(fd);
availableFDs.push(fd);
close(fd);
}
}
};