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 @@ -43,3 +43,6 @@ webserv

# superpowers working artifacts (specs + plans live locally only)
docs/superpowers/

# unit test binary
tests/unit/run_tests
22 changes: 20 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ $(TARGET): $(OBJECTS)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
@mkdir -p $(dir $@)
@echo "$(YELLOW)🔨 Compiling $<$(RESET)"
@$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
@$(CXX) $(CXXFLAGS) $(INCLUDES) -MMD -MP -c $< -o $@

# Dash prefix = silent on first build when .d files don't exist yet
-include $(OBJECTS:.o=.d)

# Clean object files
clean:
Expand All @@ -50,19 +53,34 @@ clean:
fclean: clean
@echo "$(RED)🗑️ Removing executable...$(RESET)"
@rm -f $(TARGET)
@rm -f $(UNIT_BIN)
@echo "$(GREEN)✅ Full clean complete!$(RESET)"

# Rebuild
re: fclean all

# Unit tests (phase 2.1+). Two-file build on purpose: request.cpp has no
# deps, so tests need no sockets, no server, no main.o. Same flags as the
# real build -- evaluators compile everything they find in the repo.
UNIT_SRC = tests/unit/test_request.cpp src/http/request.cpp
UNIT_BIN = tests/unit/run_tests

unit: $(UNIT_BIN)
@./$(UNIT_BIN)

$(UNIT_BIN): $(UNIT_SRC) $(INCLUDE_DIR)/http.hpp
@echo "$(CYAN)🧪 Building unit tests...$(RESET)"
@$(CXX) $(CXXFLAGS) $(INCLUDES) -o $(UNIT_BIN) $(UNIT_SRC)

# Show help
help:
@echo "$(CYAN)📚 WebServ Build Targets:$(RESET)"
@echo " 🔨 make all - Build the webserv executable (default)"
@echo " 🧹 make clean - Remove object files"
@echo " 🗑️ make fclean - Remove object files and executable"
@echo " 🔄 make re - Clean rebuild"
@echo " 🧪 make unit - Build and run unit tests"
@echo " 📚 make help - Show this help message"

# Phony targets (not files)
.PHONY: all clean fclean re help
.PHONY: all clean fclean re help unit
90 changes: 89 additions & 1 deletion includes/http.hpp
Original file line number Diff line number Diff line change
@@ -1 +1,89 @@
#pragma once
#pragma once

#include <string>
#include <map>
#include <cstddef>

// Request/Response live here per the phase 0.2 layout -- Response joins in
// task 2.2.
//
// Request is an incremental HTTP/1.1 request parser. TCP hands us a byte
// stream with no message boundaries: recv() may deliver a request whole,
// split at any byte, or glued to the next one. So we eat fragments through
// parseFromBuffer() and remember where we were between calls.
//
// Deliberately I/O-free: no sockets, no Logger, no exceptions for bad input.
// A garbage request is normal network weather, not an exceptional condition
// -- we record an HTTP status code instead (4.5 turns it into a response).
// The subject's poll/errno rules never apply here because the server loop
// does the recv()ing; we only ever see bytes. Also what makes the unit
// tests a two-file build.

class Request {
public:
Request();

// Feed raw bytes from the connection. Returns how many bytes of
// `data` THIS request consumed. Consumption stops at completion --
// leftover bytes belong to the next request on a keep-alive
// connection (2.5 re-feeds them to a fresh Request). In a terminal
// state (complete/error) nothing is consumed: returns 0.
std::size_t parseFromBuffer(const std::string& data);

bool isComplete() const;
bool hasError() const;
int getErrorCode() const; // 400/414/431/505, 0 when healthy

const std::string& getMethod() const; // "GET"
const std::string& getUri() const; // raw "/a?b=c"
const std::string& getPath() const; // "/a"
const std::string& getQuery() const; // "b=c"
const std::string& getVersion() const; // "HTTP/1.1"
const std::string& getBody() const; // "" until 3.1 adds bodies

// Case-insensitive lookup (HoSt == HOST). Returns "" when absent,
// which is also a legal value -- hasHeader() tells those apart.
// By-value return because there's no stored string to reference
// when the header is missing.
std::string getHeader(const std::string& name) const;
bool hasHeader(const std::string& name) const;

// Recycle for the next request on a keep-alive connection.
void reset();

private:
// The whole trick of this class. Lines are consumed the moment
// they're complete, so no TCP split point can change the outcome:
//
// S_REQUEST_LINE --"GET / HTTP/1.1"--> S_HEADERS
// S_HEADERS --"Name: value"--> S_HEADERS
// S_HEADERS --blank line--> S_COMPLETE
// malformed anything, from any state --> S_ERROR
//
// S_COMPLETE/S_ERROR are terminal. 3.1 slots S_BODY in after the
// blank line, 5.1 adds chunk states. Same chassis, more states.
enum ParseState {
S_REQUEST_LINE,
S_HEADERS,
S_COMPLETE,
S_ERROR
};

ParseState state_;
std::string buf_; // only ever holds the current partial line
int error_code_;
std::size_t header_count_;

std::string method_;
std::string uri_;
std::string path_;
std::string query_;
std::string version_;
std::string body_;
std::map<std::string, std::string> headers_; // keys lowercased

// one grammar production per state, mirroring the machine
void parseRequestLine(const std::string& line);
void parseHeaderLine(const std::string& line);
void setError(int code);
};
4 changes: 2 additions & 2 deletions kanban.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@

## PHASE 2: The Core - Basic GET & Static Files

### [ ] 2.1: HTTP Request Parsing (GET, Headers, Body Stub)
### [ completed ] 2.1: HTTP Request Parsing (GET, Headers, Body Stub)
**Assignee:** Dev B
**Description:**
- Create Request class in includes/http.hpp and src/http/request.cpp
Expand All @@ -228,7 +228,7 @@
- Validate HTTP/1.1 compliance
- Extract query string from URI (store separately)
- Methods:
- `parseFromBuffer(const std::string& buffer)` - returns bytes consumed or -1 (incomplete)
- `parseFromBuffer(const std::string& buffer)` - returns bytes consumed (always >= 0); completeness is a separate question answered by `isComplete()`
- `getMethod()`, `getUri()`, `getHeader(key)`, `getBody()`
- `isComplete()` - returns true if full request received

Expand Down
1 change: 0 additions & 1 deletion src/http/http.cpp

This file was deleted.

Loading
Loading