[Phase 2] Task 2.2: HTTP Response Construction - #17
Merged
MukhammadIbrokhimov merged 7 commits intoJun 19, 2026
Conversation
- Response mirrors Request: builds fields into the HTTP/1.1 wire format - setStatusCode() with reason-phrase lookup + explicit-reason overload - case-insensitive headers, canonical casing on output - Content-Length is body-authoritative (anti-smuggling); Date auto-filled - httpDate() hand-rolled RFC 1123 to dodge locale-dependent strftime - I/O-free like Request, so it unit-tests as a dependency-free build Related to task: 2.2
- framework-free check() suite mirroring test_request.cpp - covers default 200, reason lookup, case-insensitive headers, authoritative Content-Length, auto/preserved Date, httpDate(0) anchor - make unit now builds+runs request and response suites separately Related to task: 2.2
There was a problem hiding this comment.
Pull request overview
This PR introduces an HTTP/1.1 Response builder to complement the existing Request parser, enabling the server loop (Phase 2.5) to serialize status/headers/body into wire-format bytes without performing any I/O.
Changes:
- Add
Responseclass declaration toincludes/http.hppand implement serialization/date/reason-phrase helpers insrc/http/response.cpp. - Add a new framework-free unit test suite for
Responseconstruction and serialization. - Extend
make unitto build and run request/response unit binaries separately; update kanban status for Task 2.2.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| includes/http.hpp | Declares the new Response API (status/headers/body + serialize, httpDate, reasonPhrase). |
| src/http/response.cpp | Implements header canonicalization, serialization, Content-Length override, and Date formatting. |
| tests/unit/test_response.cpp | Adds unit tests validating default response, header behavior, Content-Length/Date behavior, and helper tables. |
| Makefile | Splits unit tests into separate request/response binaries and cleans both on fclean. |
| kanban.md | Marks Task 2.2 as completed and checks off DoD items. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
request.cpp and response.cpp had byte-identical copies of the hand-rolled ASCII lowercaser. Moved both into string_utils.hpp as inline (header lands in several TUs) and dropped the local statics. make unit now also depends on string_utils.hpp so editing it retriggers the suites.
config.cpp, parser.cpp and main.cpp each had the same ifstream+rdbuf body. Pulled it into a non-throwing bool readFileToString -- the three callers keep their own failure policy (two ConfigException flavours, one cerr+exit), and 2.3's static handler can reuse it for 404/403. Dropped the now-unused <fstream> (and <sstream> from main.cpp); rewrote parser.cpp's stale 'copy is cheaper than header churn' comment.
Addresses Copilot review on PR #17: - setHeader drops any header whose name (CTL/SP/HTAB/':') or value (CTL except HTAB) would break framing -- response splitting via a CGI passthrough (3.3) or error page. Mirrors what the request parser already refuses to accept, at the same boundary. - two-arg setStatusCode falls back to the table phrase when the reason carries a CTL, so attacker bytes can't split the status line. - httpDate guards a NULL gmtime() (out-of-range time_t); serialize() then omits Date rather than emit an empty one. No-crash rule holds. - 4 new injection tests; tab-in-value kept legal to avoid over-rejecting. Related to task: 2.2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Task
Description
Adds the
Responseclass — the mirror of theRequestparser from 2.1. WhereRequestdecodes a byte stream into fields,Responsebuilds fields into the HTTP/1.1 wire format.serialize()is the only thing the 2.5 server loop needs to hand to the write buffer. Kept I/O-free (no sockets, no Logger) likeRequest, so it unit-tests as a dependency-free two-file build.Changes
includes/http.hpp:Responsedeclaration (status/headers/body +serialize,httpDate,reasonPhrase)src/http/response.cpp: implementationsetStatusCode(code)looks up the reason phrase;setStatusCode(code, reason)is the explicit-reason escape hatchserialize()overrides any caller-set value so a wrong length can't desync framing (smuggling guard)httpDate()hand-rolls RFC 1123 to avoid locale-dependentstrftime %a/%bResponseis already a valid empty200 OKtests/unit/test_response.cpp: 22 framework-free assertions in thetest_request.cppstyleMakefile:make unitnow builds and runs the request and response suites separately;fcleanremoves both binariesTesting
make re,-Wall -Wextra -Werror -std=c++98)make unit: response 22 passed, 0 failed; request 88 passed, 0 failed