#ifndef __HTTPRequest_h__ #define __HTTPRequest_h__ #include "Parser.h" #include "LineParser.h" #include "IMFHeader.h" #include "CGIFormData.h" #include "MString.h" #include namespace coreutils { /// /// HTTPRequest parses a full HTTP/1.1 request - request line, headers, /// and (for a Content-Length body) either a raw body or, for /// application/x-www-form-urlencoded / multipart/form-data, a parsed /// CGIFormData - off a shared StreamReader. Resumable across any number /// of StreamReader::feed() calls, exactly like every other Parser in /// this subsystem: built by chaining LineParser (request line) and /// IMFHeader (headers) via checkParse(), the same way CGIFormData itself /// chains IMFHeader/IMFBoundary over one shared StreamReader. /// /// This codebase predates ever parsing raw HTTP off a socket (it has /// only ever sat behind a CGI gateway) - IMFRequest/IMFResponse/ /// IMFMessage are stubs from before this Parser/StreamReader subsystem /// existed and are not built on here. /// /// v1 limitations, deliberately not addressed (flagged, not fixed - /// matching the standard already set by other Parser subclasses here for /// known-but-out-of-scope gaps): /// - chunked Transfer-Encoding is not supported, only Content-Length. /// - repeated header names (e.g. multiple Cookie: lines) last-wins - /// the same limitation IMFHeader's std::map already has, inherited /// here since HEADERS delegates to IMFHeader directly. /// class HTTPRequest : public Parser { public: HTTPRequest(StreamReader &reader); virtual ~HTTPRequest(); /// /// Valid once doParse() has returned Status::COMPLETE. /// MString getMethod(); MString getPath(); MString getQueryString(); MString getVersion(); /// /// Case-insensitive header lookup (HTTP header names are /// case-insensitive per RFC 7230, unlike IMFHeader's underlying /// std::map, which compares byte-for-byte) - returns an empty /// MString if the header wasn't present. Valid once the HEADERS /// state has completed (i.e. from BODY parsing onward). /// MString getHeader(const char *name); /// /// Every header as parsed, keyed exactly as received (not /// case-normalized) - prefer getHeader() for lookups. /// std::map &getHeaders(); /// /// Valid once doParse() reports COMPLETE. Populated only when the /// request had a body that was neither application/ /// x-www-form-urlencoded nor multipart/form-data (e.g. a raw JSON /// body) - empty otherwise, including when there was no body at all. /// MString getBody(); /// /// Valid once doParse() reports COMPLETE, when the body's /// Content-Type was application/x-www-form-urlencoded or /// multipart/form-data - NULL otherwise (including no body at all). /// Owned by this HTTPRequest - do not delete. /// CGIFormData *getFormData(); protected: Status parse() override; private: enum State { REQUEST_LINE, HEADERS, BODY, DONE } state = REQUEST_LINE; MString method, path, queryString, version; std::map headers; MString body; MString boundary; LineParser *lineParser = NULL; IMFHeader *headerParser = NULL; CGIFormData *formParser = NULL; }; } #endif