Skip to content

refactor(http): HTTP layer full refactor — parser hardening, DRY, helpers#27

Merged
byronlove111 merged 20 commits into
mainfrom
feat/http-parser-refacto
Jun 20, 2026
Merged

refactor(http): HTTP layer full refactor — parser hardening, DRY, helpers#27
byronlove111 merged 20 commits into
mainfrom
feat/http-parser-refacto

Conversation

@byronlove111

Copy link
Copy Markdown
Collaborator

Summary

Refactoring complet du HTTP layer (parser, router, handlers, response, CGI utils) sans toucher à l'architecture CGI asynchrone (voir issue #26).

  • Parser hardened : single-pass, ParseException avec code HTTP, validation exhaustive comparée à NGINX (méthode casse, version, Host, Content-Length, doublons, tab après colon)
  • Headers normalisés en lowercase : fix bug case-sensitive Content-Length/content-length — tous les lookups fonctionnent quelle que soit la casse client
  • DRY : buildHttpError, buildHttpOk, buildHttpCreated, buildHttpNoContent, buildRedirect extraits dans src/http/builders/ — plus de construction manuelle de réponses dans les handlers
  • extractUriPath centralisé dans StringUtils — élimine 4 duplications dans GetHandler, PostHandler, DeleteHandler, CGI
  • readFdToString / writeFdFromString dans HttpUtils — élimine les boucles read()/write() dupliquées
  • processHttp facade : parse → route → handle → build en un seul point d'entrée
  • Server: webserv/1.0 ajouté à toutes les réponses
  • Makefile : objets dans obj/, plus de pollution dans src/
  • Comments : inline comments sur parser, router, handlers, response builder

Test plan

  • Build sans erreur (make re)
  • 17/18 cas NGINX passent (seul HTTP/0.9 sans version diverge — acceptable)
  • GET / POST / DELETE fonctionnels
  • Headers case-insensitive validés (host, content-length, HOST, CONTENT-LENGTH)
  • Status codes corrects : 200, 201, 204, 301, 400, 403, 404, 405, 413, 500, 505

Made with Cursor

byronlove111 and others added 20 commits June 18, 2026 22:26
…alid

- Introduce RequestParser::ParseException with status code (400/505)
- isValid() now throws instead of returning bool, distinguishing
  malformed requests (400) from unsupported versions (505)
- Add processHttp() as single entry point for the HTTP layer:
  raw request + ServerConfig in, serialized response string out
- Fix case-insensitive Host header check (host/HOST/HoSt all valid)
- Add inline comments with bad-request examples on each validation check

Co-authored-by: Cursor <cursoragent@cursor.com>
- Merge isValid() into parse() for a single scan of the raw request
- Split parseHeaders() into parseHeaderLine() and validateHostHeader()
- Add lowercase method check (NGINX rejects non-uppercase methods)
- Add Host value empty/whitespace check
- Add duplicate Host header detection
- Add duplicate Content-Length detection (request smuggling)
- Add comments with bad-request examples on each validation check

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…positive integer

Co-authored-by: Cursor <cursoragent@cursor.com>
…ory and adjust clean target

- Changed object file output path from current directory to 'obj/' for better organization.
- Updated clean target to remove the 'obj' directory instead of individual object files.
- Added 'obj/' to .gitignore to prevent tracking of generated object files.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…nstruction

Co-authored-by: Cursor <cursoragent@cursor.com>
…in applyCustomErrorPage

- ssize_t bytesRead uninitialized when file is empty → undefined behavior on if (bytesRead < 0)
- location.getRoot().empty() guard was skipping error page lookup for locations without
  root (CGI, redirect) even when open() would gracefully handle the missing path itself

Co-authored-by: Cursor <cursoragent@cursor.com>
… applyCustomErrorPage

Co-authored-by: Cursor <cursoragent@cursor.com>
Chaque builder HTTP a son propre fichier dans src/http/builders/.
HttpUtils.cpp ne contient plus que les vrais utilitaires (readFdToString, getContentType).
Umbrella header HttpBuilders.hpp pour importer tous les builders en un include.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Introduced `extractUriPath` to handle query string removal from URIs in both DeleteHandler and PostHandler.
- Added `deleteFile` function to encapsulate file existence check and deletion logic in DeleteHandler.
- Updated `handleDelete` and `handlePost` methods to utilize the new utility functions for cleaner code structure.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Implemented `extractUriPath` in StringUtils to streamline URI query string removal.
- Updated DeleteHandler, GetHandler, and PostHandler to utilize the new utility function, enhancing code clarity and reducing redundancy.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ile descriptors

- Added `writeFdFromString` function to `HttpUtils` for writing string data to file descriptors in a loop until completion.
- Updated `PostHandler` to utilize the new utility function for writing request bodies, improving code clarity and error handling.
- Enhanced `execute` method in `CgiHandler` to streamline the process of sending request body data to CGI scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Adjusted the closing brace indentation in the processHttp function for improved code readability and consistency.
Headers are case-insensitive per HTTP/1.1. Store all keys in lowercase
so that lookups in parseBody (content-length) and env.cpp (content-type,
host) work regardless of client casing.

Also fix tolower() call to cast through unsigned char to avoid UB on
high-ASCII characters.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Replace PascalCase lookups (Host, Content-Length, Accept, etc.) with
  lowercase equivalents to match the new storage convention in parseHeaders
- Wrap invalid-request tests in try-catch to handle ParseException instead
  of expecting an empty HttpRequest on parsing failure
- Create /tmp/www/errors/404.html in test_custom_error_page main() so the
  custom error page test passes in CI without pre-existing fixtures

Co-authored-by: Cursor <cursoragent@cursor.com>
- test_adversarial: add safeParse() helper to absorb ParseException in
  all invalid-request parse tests; update makeReq to use lowercase keys
- test_cgi_advanced: makeGet/makePost now store headers with lowercase
  keys (host, content-type) to match parseHeaders normalization
- test_post_handler: POST with empty body now expects 201 (file created)
  instead of 400 — empty body is valid HTTP, check was intentionally removed

Co-authored-by: Cursor <cursoragent@cursor.com>
@byronlove111 byronlove111 merged commit ffdf113 into main Jun 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant