Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ webserv

# debug information files
*.dwo

# superpowers working artifacts (specs + plans live locally only)
docs/superpowers/
7 changes: 7 additions & 0 deletions config/test_bad_syntax.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
server {
listen 8080
server_name broken;
root ./www;
location / {
allowed_methods GET;
}
12 changes: 12 additions & 0 deletions config/test_duplicate.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
server {
listen 8080;
server_name a;
root ./www;
location / { allowed_methods GET; }
}
server {
listen 8080;
server_name b;
root ./www;
location / { allowed_methods GET; }
}
12 changes: 12 additions & 0 deletions config/test_multi_port.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
server {
listen 8080;
listen 8081;
server_name localhost;
root ./www;
client_max_body_size 10M;

location / {
allowed_methods GET;
index index.html;
}
}
13 changes: 13 additions & 0 deletions config/test_two_servers.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
server {
listen 8090;
server_name a;
root ./www;
location / { allowed_methods GET; }
}

server {
listen 9090;
server_name b;
root ./www;
location / { allowed_methods GET; }
}
40 changes: 29 additions & 11 deletions includes/server.hpp
Original file line number Diff line number Diff line change
@@ -1,37 +1,55 @@
#pragma once

#include "socket.hpp"
#include "config.hpp"
#include <vector>
#include <map>
#include <poll.h>
#include <iostream>
#include <unistd.h>
#include <csignal>

#include "string_utils.hpp"

class Socket;

extern volatile sig_atomic_t g_shutdown;

class Server {
private:
std::vector<struct pollfd> poll_fds;
// Reference, not value: Server borrows the listening Socket owned
// by main(). With ownership in main, only main's destructor calls
// ::close() on the listening fd, avoiding a double close.
Socket &socket;

// Server is not copyable: a reference member would alias the same
// Socket anyway, and there is exactly one Server in main().
// Phase 1.5 members --------------------------------------------------
// `config_` is a borrow: it lives in main()'s stack frame, longer
// than this Server, so the pointer is safe for our lifetime.
// Phase 2.4's router will read fd_to_server_ to know which server
// block a request lands on.
//
// INVARIANT: `Config` must NOT be mutated after `Config::load()` for
// the lifetime of this Server. fd_to_server_ stores raw pointers to
// elements of cfg.servers()'s internal vector; any push_back would
// reallocate that vector and invalidate every pointer in the map.
// Today main() loads cfg once and never touches it again — keep that.
const Config* config_;
std::vector<Socket*> listeners_;
std::map<int, const ServerConfig*> fd_to_server_;
// Parallel map keyed on the same listener fds as fd_to_server_. Lets
// run() jump straight to the owning Socket on POLLIN instead of
// scanning listeners_ linearly — matters once Phase 4's stress test
// pushes the listener count up.
std::map<int, Socket*> fd_to_listener_;
// --------------------------------------------------------------------

Server(const Server&);
Server& operator=(const Server&);

public:
Server(Socket &_socket);
// Phase 1.5 ctor. Walks cfg.servers(), opens one Socket per
// ListenSpec, populates fd_to_server_. Partial-init failures are
// cleaned up inside the ctor so the caller never sees a half-built
// Server (ctor exceptions skip the dtor).
Server(const Config& cfg);

~Server();
void run();
// Returns false when the client has closed the connection (or recv
// failed) so the poll loop knows to remove it. True means keep the fd.
bool handle_client_data_read(int client_fd);
void handle_client_data_write(int client_fd);
void cleanup_sockets();
Expand Down
10 changes: 9 additions & 1 deletion includes/socket.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <string>
#include <fcntl.h>
Expand All @@ -22,7 +23,14 @@ class Socket {
Socket& operator=(const Socket&);

public:
Socket(int port);
// Phase 1.5: config-driven listener constructor.
// Resolves `host` (a numeric IPv4 string like "0.0.0.0" or "127.0.0.1")
// via getaddrinfo with AI_NUMERICHOST so no DNS happens. Sets
// SO_REUSEADDR before bind so we can restart inside TIME_WAIT without
// EADDRINUSE. Throws SocketException on any setup failure; the body
// guarantees the fd and addrinfo chain are released on every throw
// path (see socket.cpp for the cleanup contract).
Socket(const std::string& host, int port);
~Socket();
void startListening(int backlog = SOMAXCONN);
void close();
Expand Down
10 changes: 5 additions & 5 deletions kanban.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@

---

### [ ] 1.5: Load Config & Initialize Server on Startup
### [ Completed ] 1.5: Load Config & Initialize Server on Startup
**Assignee:** Dev B
**Description:**
- Implement `main(int argc, char** argv)`
Expand All @@ -207,10 +207,10 @@
- Graceful error handling: print to stderr and exit(1) on config error

**DoD:**
- [ ] `./webserv` loads default.conf
- [ ] `./webserv custom.conf` loads custom config
- [ ] All listening ports open successfully
- [ ] Error messages clear on config failure
- [x] `./webserv` loads default.conf (path: `config/default.conf`)
- [x] `./webserv custom.conf` loads custom config
- [x] All listening ports open successfully
- [x] Error messages clear on config failure

---

Expand Down
25 changes: 16 additions & 9 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,6 @@ static int dumpConfig(const std::string& path) {
// --- end DEV-ONLY ---------------------------------------------------------

int main(int argc, char **argv) {
(void)argc;
(void)argv;

if (getenv("DEBUG")) {
Logger::setLevel(Logger::DEBUG);
LOG_INFO("Debug mode enabled.");
Expand All @@ -104,10 +101,20 @@ int main(int argc, char **argv) {
if (const char* path = getenv("CONFIG_DUMP"))
return dumpConfig(path);

std::cout << "WebServ starting..." << std::endl;
Socket server_socket(8080);
server_socket.startListening();
Server web_server(server_socket);
web_server.run();
if (argc > 2) {
std::cerr << "usage: ./webserv [config_file]" << std::endl;
return 1;
}
const std::string path = (argc == 2) ? argv[1] : "config/default.conf";

try {
Config cfg;
cfg.load(path);
Server srv(cfg);
srv.run();
} catch (const std::exception& e) {
std::cerr << "fatal: " << e.what() << std::endl;
return 1;
}
return 0;
}
}
Loading
Loading