Loading ccsrc/Helpers/async_process.hh +108 −13 Original line number Diff line number Diff line /*! * \file async_process.hh * \brief Header file for the asynchronous child-process manager. * \author ETSI STF685 * \copyright ETSI Copyright Notification * No part may be reproduced except as authorized by written permission. * The copyright and the foregoing restriction extend to reproduction in all media. * All rights reserved. * \version 0.1 */ #include <unistd.h> #include <sys/wait.h> #include <fcntl.h> Loading @@ -15,42 +25,127 @@ #include <vector> #include <optional> /*! * \class async_process * \brief Manages the lifecycle of an asynchronous child process with piped I/O. * * Forks and executes an arbitrary command with full stdin/stdout/stderr pipe * support, an optional execution timeout, optional sudo elevation, and an * optional streaming output callback. A background reader thread drains the * child's output pipes and invokes the callback (if set) for each chunk. * * Usage pattern: * \code * async_process proc; * proc.start("tcpdump", {"-i", "eth0"}, {}); * // ... do work ... * proc.terminate(); * proc.wait(); * \endcode * * Instances are non-copyable; move semantics are not provided. */ class async_process { public: /*! * \brief Callback type invoked for each chunk of child process output. * \param chunk A view of the output chunk (stdout or stderr). * \param isStderr \c true if the chunk came from stderr, \c false for stdout. */ using OutputCallback = std::function<void(std::string_view, bool isStderr)>; /*! * \struct Options * \brief Configuration options for \ref async_process::start(). */ struct Options { bool useSudo = false; std::string sudoPassword; // only used if useSudo == true OutputCallback onOutput; // optional callback for stdout/stderr chunks bool useSudo = false; //!< Run the command under \c sudo if \c true. std::string sudoPassword; //!< Password fed to sudo stdin; only used when \c useSudo is \c true. OutputCallback onOutput; //!< Optional callback invoked for each stdout/stderr chunk. }; /*! \brief Default constructor. */ async_process() = default; /*! \brief Destructor — terminates the child process (SIGTERM) and waits for it to exit. */ ~async_process() { terminate(); wait(); } async_process(const async_process&) = delete; async_process& operator=(const async_process&) = delete; /*! * \brief Fork and execute a command asynchronously. * * Starts the child process and, if \c opts.onOutput is set, launches a * background reader thread that drains stdout/stderr and invokes the callback. * Throws \c std::runtime_error on fork or pipe failure. * \param[in] command The executable path or name (resolved via \c PATH). * \param[in] args Command-line arguments (argv[1..n]). * \param[in] opts Execution options (sudo, output callback). * \param[in] timeout Maximum time to wait before the process is considered hung * (default 2 000 ms). Currently informational only. */ void start(const std::string& command, const std::vector<std::string>& args, Options opts, std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)); /*! * \brief Non-blocking check for child process exit. * \param[out] status The exit status if the child has exited. * \return \c true if the child has exited and \p status has been filled in. */ bool pollExitStatus(int& status); /*! * \brief Block until the child process exits. * \return The child's exit status as returned by \c waitpid(). */ int wait(); /*! * \brief Send a signal to the child and optionally wait for it to exit. * \param[in] signalNo Signal number to send (default \c SIGTERM). * \param[in] timeout How long to wait for the child to exit after the signal * (default 5 000 ms). * \return The child's exit status, or -1 if the child was not running. */ int terminate(int signalNo = SIGTERM, std::chrono::milliseconds timeout = std::chrono::milliseconds(5000)); /*! * \brief Return whether the child process is currently running. * \return \c true if the child is alive, \c false if it has exited or was never started. */ inline bool running() const noexcept { return running_ && !exited_; }; inline void killForce() { terminate(SIGTERM); }; // Clean termination of the process, with SIGTERM signal /*! \brief Send SIGTERM to the child (clean shutdown request). */ inline void killForce() { terminate(SIGTERM); }; /*! * \brief Drain and return any buffered data from the child's stdout pipe. * \return All currently available stdout data as a string. */ inline std::string readStdout() { return drainFd(stdoutPipe_[0]); }; /*! * \brief Drain and return any buffered data from the child's stderr pipe. * \return All currently available stderr data as a string. */ inline std::string readStderr() { return drainFd(stderrPipe_[0]); }; /*! * \brief Return the PID of the child process. * \return The child's \c pid_t, or -1 if no process has been started. */ inline pid_t pid() const noexcept { return pid_; }; private: pid_t pid_ = -1; bool running_ = false; bool exited_ = false; int exitStatus_; Options opts_; std::array<int, 2> stdoutPipe_{-1, -1}; std::array<int, 2> stderrPipe_{-1, -1}; std::array<int, 2> stdinPipe_{-1, -1}; std::thread reader_; pid_t pid_ = -1; //!< Child process PID bool running_ = false; //!< True once start() succeeds bool exited_ = false; //!< True once waitpid() reports exit int exitStatus_; //!< Raw waitpid() exit status Options opts_; //!< Saved options from start() std::array<int, 2> stdoutPipe_{-1, -1}; //!< [read, write] pipe for child stdout std::array<int, 2> stderrPipe_{-1, -1}; //!< [read, write] pipe for child stderr std::array<int, 2> stdinPipe_{-1, -1}; //!< [read, write] pipe for child stdin std::thread reader_; //!< Background thread that drains output pipes static void setNonBlocking(int fd); static void closeIfOpen(int fd); Loading ccsrc/Protocols/Http/http_codec.hh +139 −10 Original line number Diff line number Diff line /*! * \file http_codec.hh * \brief Header file for the HTTP/1.1 message codec. * \author ETSI STF685 * \copyright ETSI Copyright Notification * No part may be reproduced except as authorized by written permission. * The copyright and the foregoing restriction extend to reproduction in all media. * All rights reserved. * \version 0.1 */ #pragma once #include <memory> Loading Loading @@ -31,23 +41,45 @@ namespace LibHttp__JsonMessageBodyTypes { class JsonBody; } // namespace LibHttp__JsonMessageBodyTypes /*! * \struct encoding_context * \brief Transient state maintained while encoding a single HTTP message. * * Tracks whether a Content-Length header has already been written and what the * accumulated body length is, so the encoder can back-fill or omit the header * as required by RFC 7230. */ struct encoding_context { unsigned int length; uint8_t is_content_length_present; unsigned int length; //!< Accumulated encoded body length (bytes). uint8_t is_content_length_present; //!< Non-zero if Content-Length was already written. /*! \brief Default constructor — delegates to \c reset(). */ encoding_context() { reset(); }; /*! \brief Reset all fields to their initial (no-message) state. */ void reset() { length = -1; is_content_length_present = 0x00; }; }; /*! * \struct decoding_context * \brief Transient state maintained while decoding a single HTTP message. * * Tracks the expected body length (from Content-Length), whether the body * should be treated as raw binary, and whether chunked transfer encoding is * active, so the decoder can correctly delimit the message boundary. */ struct decoding_context { unsigned int length; uint8_t is_binary; bool chunked; unsigned int length; //!< Expected body length from the Content-Length header (-1 if unknown). uint8_t is_binary; //!< Non-zero if the body should be decoded as raw binary. bool chunked; //!< \c true if Transfer-Encoding: chunked was seen. /*! \brief Default constructor — delegates to \c reset(). */ decoding_context() { reset(); }; /*! \brief Reset all fields to their initial (no-message) state. */ void reset() { length = -1; is_binary = 0x00; Loading @@ -55,36 +87,133 @@ struct decoding_context { }; }; /*! * \class http_codec * \brief Base codec for encoding/decoding HTTP/1.1 messages and their entity bodies. * * Handles HTTP/1.1 framing (request-line or status-line, headers, body) and * dispatches body encoding/decoding to content-type-specific virtual methods * (binary, XML, JSON, HTML). Subclasses override the \c encode_body_* / * \c decode_body_* methods to provide concrete payload serialisation. * * The set of active body codecs is configured at runtime via * \c set_payload_codecs(). Chunked transfer encoding and multi-buffer * reassembly are handled transparently during decoding. */ class http_codec : public codec_gen<LibHttp__TypesAndValues::HttpMessage, LibHttp__TypesAndValues::HttpMessage> { encoding_context _ec; decoding_context _dc; encoding_context _ec; //!< Per-message encoding state decoding_context _dc; //!< Per-message decoding state protected: //! Map of content-type key → body codec instance used during body dispatch. std::map<std::string, std::unique_ptr<codec_gen<Record_Type, Record_Type>>> _codecs; std::vector<OCTETSTRING> _bufferized_buffers; unsigned int _initial_content_length; unsigned int _current_content_length; std::vector<OCTETSTRING> _bufferized_buffers; //!< Reassembly buffer for fragmented HTTP bodies. unsigned int _initial_content_length; //!< Content-Length value from the headers. unsigned int _current_content_length; //!< Bytes accumulated so far during decoding. public: /*! \brief Default constructor. */ explicit http_codec() : codec_gen<LibHttp__TypesAndValues::HttpMessage, LibHttp__TypesAndValues::HttpMessage>(), _ec(), _dc(), _codecs(), _bufferized_buffers(), _initial_content_length{0}, _current_content_length{0} {}; /*! \brief Destructor. */ virtual ~http_codec(){}; /*! * \brief Encode an \c HttpMessage value to an HTTP/1.1 octet string. * \param[in] p_message The HTTP message (request or response) to encode. * \param[out] p_data The encoded octet string. * \return 0 on success, -1 on failure. */ virtual int encode(const LibHttp__TypesAndValues::HttpMessage &, OCTETSTRING &p_data); /*! * \brief Decode an HTTP/1.1 octet string into an \c HttpMessage value. * \param[in] p_data The raw octet string from the transport layer. * \param[out] p_message The decoded HTTP message. * \param[in] params Optional decoding parameters (may be NULL). * \return 0 on success, -1 on failure. */ virtual int decode(const OCTETSTRING &p_data, LibHttp__TypesAndValues::HttpMessage &, params *params = NULL); /*! * \brief Configure the set of body codecs from a colon-separated descriptor string. * \param[in] p_codecs Codec descriptor (e.g. \c "json_codec:xml_codec"). */ void set_payload_codecs(const std::string& p_codecs); protected: //! \protectedsection /*! * \brief Encode a binary entity body. * \param[in] p_binary_body The binary body value. * \param[out] p_encoding_buffer Output buffer to append encoded bytes to. * \param[in] p_content_type Content-Type string. * \return \c true on success, \c false if unsupported. */ virtual bool encode_body_binary(const LibHttp__BinaryMessageBodyTypes::BinaryBody &p_binary_body, OCTETSTRING &p_encoding_buffer, const std::string& p_content_type) {return false;}; /*! * \brief Decode a binary entity body. * \param[in] p_data Raw body bytes. * \param[out] p_binary_body Decoded binary body value. * \param[in] p_content_type Content-Type string. * \return \c true on success, \c false if unsupported. */ virtual bool decode_body_binary(const OCTETSTRING &p_data, LibHttp__BinaryMessageBodyTypes::BinaryBody &p_binary_body, const std::string& p_content_type) {return false;}; /*! * \brief Encode an XML entity body. * \param[in] p_xml_body The XML body value. * \param[out] p_encoding_buffer Output buffer. * \param[in] p_content_type Content-Type string. * \return \c true on success, \c false if unsupported. */ virtual bool encode_body_xml(const LibHttp__XmlMessageBodyTypes::XmlBody &p_xml_body, OCTETSTRING &p_encoding_buffer, const std::string& p_content_type) {return false;}; /*! * \brief Decode an XML entity body. * \param[in] p_data Raw body bytes. * \param[out] p_body Decoded XML body value. * \param[in] p_content_type Content-Type string. * \param[in] p_params Optional parameters. * \return \c true on success, \c false if unsupported. */ virtual bool decode_body_xml(const OCTETSTRING &p_data, LibHttp__XmlMessageBodyTypes::XmlBody &p_body, const std::string& p_content_type, params* p_params) {return false;}; /*! * \brief Encode an HTML/text entity body. * \param[in] p_html_body The HTML character string. * \param[out] p_encoding_buffer Output buffer. * \param[in] p_content_type Content-Type string. * \return \c true on success, \c false if unsupported. */ virtual bool encode_body_html(const CHARSTRING &p_html_body, OCTETSTRING &p_encoding_buffer, const std::string& p_content_type) {return false;}; /*! * \brief Decode an HTML/text entity body. * \param[in] p_data Raw body bytes. * \param[out] p_html_body Decoded HTML character string. * \param[in] p_content_type Content-Type string. * \param[in] p_params Optional parameters. * \return \c true on success, \c false if unsupported. */ virtual bool decode_body_html(const OCTETSTRING &p_data, CHARSTRING &p_html_body, const std::string& p_content_type, params* p_params); /*! * \brief Encode a JSON entity body. * \param[in] p_json_body The JSON body value. * \param[out] p_encoding_buffer Output buffer. * \param[in] p_content_type Content-Type string. * \return \c true on success, \c false if unsupported. */ virtual bool encode_body_json(const LibHttp__JsonMessageBodyTypes::JsonBody &p_json_body, OCTETSTRING &p_encoding_buffer, const std::string& p_content_type) {return false;}; /*! * \brief Decode a JSON entity body. * \param[in] p_data Raw body bytes. * \param[out] p_json_body Decoded JSON body value. * \param[in] p_content_type Content-Type string. * \param[in] p_params Optional parameters. * \return \c true on success, \c false if unsupported. */ virtual bool decode_body_json(const OCTETSTRING &p_data, LibHttp__JsonMessageBodyTypes::JsonBody &p_json_body, const std::string& p_content_type, params* p_params) {return false;}; private: Loading ccsrc/Protocols/Http/http_layer.cc +155 −29 File changed.Preview size limit exceeded, changes collapsed. Show changes ccsrc/Protocols/Http/http_layer.hh +57 −0 Original line number Diff line number Diff line Loading @@ -10,7 +10,9 @@ */ #pragma once #include <map> #include <memory> #include <string> #include "t_layer.hh" Loading @@ -37,6 +39,61 @@ protected: //! \protectedsection bool _device_mode; public: //! \publicsection /*! * \brief Map of HTTP status code (as a string, e.g. \c "404") to its * standard reason phrase (e.g. \c "Not Found"), per RFC 7231 and the * IANA HTTP Status Code Registry. Used to fill in the reason phrase * of a status line when one was not otherwise provided. */ static inline const std::map<std::string, std::string> status_text = { {"100", "Continue"}, {"101", "Switching Protocols"}, {"200", "OK"}, {"201", "Created"}, {"202", "Accepted"}, {"203", "Non-Authoritative Information"}, {"204", "No Content"}, {"205", "Reset Content"}, {"206", "Partial Content"}, {"300", "Multiple Choices"}, {"301", "Moved Permanently"}, {"302", "Found"}, {"303", "See Other"}, {"304", "Not Modified"}, {"305", "Use Proxy"}, {"307", "Temporary Redirect"}, {"308", "Permanent Redirect"}, {"400", "Bad Request"}, {"401", "Unauthorized"}, {"402", "Payment Required"}, {"403", "Forbidden"}, {"404", "Not Found"}, {"405", "Method Not Allowed"}, {"406", "Not Acceptable"}, {"407", "Proxy Authentication Required"}, {"408", "Request Timeout"}, {"409", "Conflict"}, {"410", "Gone"}, {"411", "Length Required"}, {"412", "Precondition Failed"}, {"413", "Payload Too Large"}, {"414", "URI Too Long"}, {"415", "Unsupported Media Type"}, {"416", "Range Not Satisfiable"}, {"417", "Expectation Failed"}, {"426", "Upgrade Required"}, {"428", "Precondition Required"}, {"429", "Too Many Requests"}, {"431", "Request Header Fields Too Large"}, {"500", "Internal Server Error"}, {"501", "Not Implemented"}, {"502", "Bad Gateway"}, {"503", "Service Unavailable"}, {"504", "Gateway Timeout"}, {"505", "HTTP Version Not Supported"}, }; /*! * \brief Specialised constructor * Create a new instance of the http_layer class Loading ccsrc/Protocols/Json/json_codec.hh +35 −0 Original line number Diff line number Diff line /*! * \file json_codec.hh * \brief Header file for the JSON HTTP message body codec. * \author ETSI STF685 * \copyright ETSI Copyright Notification * No part may be reproduced except as authorized by written permission. * The copyright and the foregoing restriction extend to reproduction in all media. * All rights reserved. * \version 0.1 */ #pragma once #include "codec_gen.hh" Loading @@ -11,13 +21,38 @@ namespace LibHttp__JsonMessageBodyTypes { class JsonBody; } /*! * \class json_codec * \brief Codec for JSON-encoded HTTP message bodies. * * Encodes a TITAN \c JsonBody value to a UTF-8 JSON octet string and decodes * a UTF-8 JSON octet string back to a \c JsonBody value. Registered with * \ref codec_stack_builder under the key \c "json_codec" and instantiated by * \c json_codec_factory when the layer stack descriptor includes that key. */ class json_codec: public codec_gen<LibHttp__JsonMessageBodyTypes::JsonBody, LibHttp__JsonMessageBodyTypes::JsonBody> { public: /*! \brief Default constructor. */ explicit json_codec() : codec_gen<LibHttp__JsonMessageBodyTypes::JsonBody, LibHttp__JsonMessageBodyTypes::JsonBody>() { }; /*! \brief Destructor. */ virtual ~json_codec() { }; /*! * \brief Encode a JSON message body to a UTF-8 octet string. * \param[in] p_body The JSON body value to encode. * \param[out] data The resulting UTF-8 JSON octet string. * \return 0 on success, -1 on failure. */ virtual int encode (const LibHttp__JsonMessageBodyTypes::JsonBody&, OCTETSTRING& data); /*! * \brief Decode a UTF-8 JSON octet string into a JSON message body. * \param[in] p_data The JSON octet string to decode. * \param[out] p_body The decoded JSON body value. * \param[in] p_params Optional decoding parameters (may be NULL). * \return 0 on success, -1 on failure. */ virtual int decode (const OCTETSTRING& p_data, LibHttp__JsonMessageBodyTypes::JsonBody&, params* p_params = NULL); }; // End of class json_codec Loading
ccsrc/Helpers/async_process.hh +108 −13 Original line number Diff line number Diff line /*! * \file async_process.hh * \brief Header file for the asynchronous child-process manager. * \author ETSI STF685 * \copyright ETSI Copyright Notification * No part may be reproduced except as authorized by written permission. * The copyright and the foregoing restriction extend to reproduction in all media. * All rights reserved. * \version 0.1 */ #include <unistd.h> #include <sys/wait.h> #include <fcntl.h> Loading @@ -15,42 +25,127 @@ #include <vector> #include <optional> /*! * \class async_process * \brief Manages the lifecycle of an asynchronous child process with piped I/O. * * Forks and executes an arbitrary command with full stdin/stdout/stderr pipe * support, an optional execution timeout, optional sudo elevation, and an * optional streaming output callback. A background reader thread drains the * child's output pipes and invokes the callback (if set) for each chunk. * * Usage pattern: * \code * async_process proc; * proc.start("tcpdump", {"-i", "eth0"}, {}); * // ... do work ... * proc.terminate(); * proc.wait(); * \endcode * * Instances are non-copyable; move semantics are not provided. */ class async_process { public: /*! * \brief Callback type invoked for each chunk of child process output. * \param chunk A view of the output chunk (stdout or stderr). * \param isStderr \c true if the chunk came from stderr, \c false for stdout. */ using OutputCallback = std::function<void(std::string_view, bool isStderr)>; /*! * \struct Options * \brief Configuration options for \ref async_process::start(). */ struct Options { bool useSudo = false; std::string sudoPassword; // only used if useSudo == true OutputCallback onOutput; // optional callback for stdout/stderr chunks bool useSudo = false; //!< Run the command under \c sudo if \c true. std::string sudoPassword; //!< Password fed to sudo stdin; only used when \c useSudo is \c true. OutputCallback onOutput; //!< Optional callback invoked for each stdout/stderr chunk. }; /*! \brief Default constructor. */ async_process() = default; /*! \brief Destructor — terminates the child process (SIGTERM) and waits for it to exit. */ ~async_process() { terminate(); wait(); } async_process(const async_process&) = delete; async_process& operator=(const async_process&) = delete; /*! * \brief Fork and execute a command asynchronously. * * Starts the child process and, if \c opts.onOutput is set, launches a * background reader thread that drains stdout/stderr and invokes the callback. * Throws \c std::runtime_error on fork or pipe failure. * \param[in] command The executable path or name (resolved via \c PATH). * \param[in] args Command-line arguments (argv[1..n]). * \param[in] opts Execution options (sudo, output callback). * \param[in] timeout Maximum time to wait before the process is considered hung * (default 2 000 ms). Currently informational only. */ void start(const std::string& command, const std::vector<std::string>& args, Options opts, std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)); /*! * \brief Non-blocking check for child process exit. * \param[out] status The exit status if the child has exited. * \return \c true if the child has exited and \p status has been filled in. */ bool pollExitStatus(int& status); /*! * \brief Block until the child process exits. * \return The child's exit status as returned by \c waitpid(). */ int wait(); /*! * \brief Send a signal to the child and optionally wait for it to exit. * \param[in] signalNo Signal number to send (default \c SIGTERM). * \param[in] timeout How long to wait for the child to exit after the signal * (default 5 000 ms). * \return The child's exit status, or -1 if the child was not running. */ int terminate(int signalNo = SIGTERM, std::chrono::milliseconds timeout = std::chrono::milliseconds(5000)); /*! * \brief Return whether the child process is currently running. * \return \c true if the child is alive, \c false if it has exited or was never started. */ inline bool running() const noexcept { return running_ && !exited_; }; inline void killForce() { terminate(SIGTERM); }; // Clean termination of the process, with SIGTERM signal /*! \brief Send SIGTERM to the child (clean shutdown request). */ inline void killForce() { terminate(SIGTERM); }; /*! * \brief Drain and return any buffered data from the child's stdout pipe. * \return All currently available stdout data as a string. */ inline std::string readStdout() { return drainFd(stdoutPipe_[0]); }; /*! * \brief Drain and return any buffered data from the child's stderr pipe. * \return All currently available stderr data as a string. */ inline std::string readStderr() { return drainFd(stderrPipe_[0]); }; /*! * \brief Return the PID of the child process. * \return The child's \c pid_t, or -1 if no process has been started. */ inline pid_t pid() const noexcept { return pid_; }; private: pid_t pid_ = -1; bool running_ = false; bool exited_ = false; int exitStatus_; Options opts_; std::array<int, 2> stdoutPipe_{-1, -1}; std::array<int, 2> stderrPipe_{-1, -1}; std::array<int, 2> stdinPipe_{-1, -1}; std::thread reader_; pid_t pid_ = -1; //!< Child process PID bool running_ = false; //!< True once start() succeeds bool exited_ = false; //!< True once waitpid() reports exit int exitStatus_; //!< Raw waitpid() exit status Options opts_; //!< Saved options from start() std::array<int, 2> stdoutPipe_{-1, -1}; //!< [read, write] pipe for child stdout std::array<int, 2> stderrPipe_{-1, -1}; //!< [read, write] pipe for child stderr std::array<int, 2> stdinPipe_{-1, -1}; //!< [read, write] pipe for child stdin std::thread reader_; //!< Background thread that drains output pipes static void setNonBlocking(int fd); static void closeIfOpen(int fd); Loading
ccsrc/Protocols/Http/http_codec.hh +139 −10 Original line number Diff line number Diff line /*! * \file http_codec.hh * \brief Header file for the HTTP/1.1 message codec. * \author ETSI STF685 * \copyright ETSI Copyright Notification * No part may be reproduced except as authorized by written permission. * The copyright and the foregoing restriction extend to reproduction in all media. * All rights reserved. * \version 0.1 */ #pragma once #include <memory> Loading Loading @@ -31,23 +41,45 @@ namespace LibHttp__JsonMessageBodyTypes { class JsonBody; } // namespace LibHttp__JsonMessageBodyTypes /*! * \struct encoding_context * \brief Transient state maintained while encoding a single HTTP message. * * Tracks whether a Content-Length header has already been written and what the * accumulated body length is, so the encoder can back-fill or omit the header * as required by RFC 7230. */ struct encoding_context { unsigned int length; uint8_t is_content_length_present; unsigned int length; //!< Accumulated encoded body length (bytes). uint8_t is_content_length_present; //!< Non-zero if Content-Length was already written. /*! \brief Default constructor — delegates to \c reset(). */ encoding_context() { reset(); }; /*! \brief Reset all fields to their initial (no-message) state. */ void reset() { length = -1; is_content_length_present = 0x00; }; }; /*! * \struct decoding_context * \brief Transient state maintained while decoding a single HTTP message. * * Tracks the expected body length (from Content-Length), whether the body * should be treated as raw binary, and whether chunked transfer encoding is * active, so the decoder can correctly delimit the message boundary. */ struct decoding_context { unsigned int length; uint8_t is_binary; bool chunked; unsigned int length; //!< Expected body length from the Content-Length header (-1 if unknown). uint8_t is_binary; //!< Non-zero if the body should be decoded as raw binary. bool chunked; //!< \c true if Transfer-Encoding: chunked was seen. /*! \brief Default constructor — delegates to \c reset(). */ decoding_context() { reset(); }; /*! \brief Reset all fields to their initial (no-message) state. */ void reset() { length = -1; is_binary = 0x00; Loading @@ -55,36 +87,133 @@ struct decoding_context { }; }; /*! * \class http_codec * \brief Base codec for encoding/decoding HTTP/1.1 messages and their entity bodies. * * Handles HTTP/1.1 framing (request-line or status-line, headers, body) and * dispatches body encoding/decoding to content-type-specific virtual methods * (binary, XML, JSON, HTML). Subclasses override the \c encode_body_* / * \c decode_body_* methods to provide concrete payload serialisation. * * The set of active body codecs is configured at runtime via * \c set_payload_codecs(). Chunked transfer encoding and multi-buffer * reassembly are handled transparently during decoding. */ class http_codec : public codec_gen<LibHttp__TypesAndValues::HttpMessage, LibHttp__TypesAndValues::HttpMessage> { encoding_context _ec; decoding_context _dc; encoding_context _ec; //!< Per-message encoding state decoding_context _dc; //!< Per-message decoding state protected: //! Map of content-type key → body codec instance used during body dispatch. std::map<std::string, std::unique_ptr<codec_gen<Record_Type, Record_Type>>> _codecs; std::vector<OCTETSTRING> _bufferized_buffers; unsigned int _initial_content_length; unsigned int _current_content_length; std::vector<OCTETSTRING> _bufferized_buffers; //!< Reassembly buffer for fragmented HTTP bodies. unsigned int _initial_content_length; //!< Content-Length value from the headers. unsigned int _current_content_length; //!< Bytes accumulated so far during decoding. public: /*! \brief Default constructor. */ explicit http_codec() : codec_gen<LibHttp__TypesAndValues::HttpMessage, LibHttp__TypesAndValues::HttpMessage>(), _ec(), _dc(), _codecs(), _bufferized_buffers(), _initial_content_length{0}, _current_content_length{0} {}; /*! \brief Destructor. */ virtual ~http_codec(){}; /*! * \brief Encode an \c HttpMessage value to an HTTP/1.1 octet string. * \param[in] p_message The HTTP message (request or response) to encode. * \param[out] p_data The encoded octet string. * \return 0 on success, -1 on failure. */ virtual int encode(const LibHttp__TypesAndValues::HttpMessage &, OCTETSTRING &p_data); /*! * \brief Decode an HTTP/1.1 octet string into an \c HttpMessage value. * \param[in] p_data The raw octet string from the transport layer. * \param[out] p_message The decoded HTTP message. * \param[in] params Optional decoding parameters (may be NULL). * \return 0 on success, -1 on failure. */ virtual int decode(const OCTETSTRING &p_data, LibHttp__TypesAndValues::HttpMessage &, params *params = NULL); /*! * \brief Configure the set of body codecs from a colon-separated descriptor string. * \param[in] p_codecs Codec descriptor (e.g. \c "json_codec:xml_codec"). */ void set_payload_codecs(const std::string& p_codecs); protected: //! \protectedsection /*! * \brief Encode a binary entity body. * \param[in] p_binary_body The binary body value. * \param[out] p_encoding_buffer Output buffer to append encoded bytes to. * \param[in] p_content_type Content-Type string. * \return \c true on success, \c false if unsupported. */ virtual bool encode_body_binary(const LibHttp__BinaryMessageBodyTypes::BinaryBody &p_binary_body, OCTETSTRING &p_encoding_buffer, const std::string& p_content_type) {return false;}; /*! * \brief Decode a binary entity body. * \param[in] p_data Raw body bytes. * \param[out] p_binary_body Decoded binary body value. * \param[in] p_content_type Content-Type string. * \return \c true on success, \c false if unsupported. */ virtual bool decode_body_binary(const OCTETSTRING &p_data, LibHttp__BinaryMessageBodyTypes::BinaryBody &p_binary_body, const std::string& p_content_type) {return false;}; /*! * \brief Encode an XML entity body. * \param[in] p_xml_body The XML body value. * \param[out] p_encoding_buffer Output buffer. * \param[in] p_content_type Content-Type string. * \return \c true on success, \c false if unsupported. */ virtual bool encode_body_xml(const LibHttp__XmlMessageBodyTypes::XmlBody &p_xml_body, OCTETSTRING &p_encoding_buffer, const std::string& p_content_type) {return false;}; /*! * \brief Decode an XML entity body. * \param[in] p_data Raw body bytes. * \param[out] p_body Decoded XML body value. * \param[in] p_content_type Content-Type string. * \param[in] p_params Optional parameters. * \return \c true on success, \c false if unsupported. */ virtual bool decode_body_xml(const OCTETSTRING &p_data, LibHttp__XmlMessageBodyTypes::XmlBody &p_body, const std::string& p_content_type, params* p_params) {return false;}; /*! * \brief Encode an HTML/text entity body. * \param[in] p_html_body The HTML character string. * \param[out] p_encoding_buffer Output buffer. * \param[in] p_content_type Content-Type string. * \return \c true on success, \c false if unsupported. */ virtual bool encode_body_html(const CHARSTRING &p_html_body, OCTETSTRING &p_encoding_buffer, const std::string& p_content_type) {return false;}; /*! * \brief Decode an HTML/text entity body. * \param[in] p_data Raw body bytes. * \param[out] p_html_body Decoded HTML character string. * \param[in] p_content_type Content-Type string. * \param[in] p_params Optional parameters. * \return \c true on success, \c false if unsupported. */ virtual bool decode_body_html(const OCTETSTRING &p_data, CHARSTRING &p_html_body, const std::string& p_content_type, params* p_params); /*! * \brief Encode a JSON entity body. * \param[in] p_json_body The JSON body value. * \param[out] p_encoding_buffer Output buffer. * \param[in] p_content_type Content-Type string. * \return \c true on success, \c false if unsupported. */ virtual bool encode_body_json(const LibHttp__JsonMessageBodyTypes::JsonBody &p_json_body, OCTETSTRING &p_encoding_buffer, const std::string& p_content_type) {return false;}; /*! * \brief Decode a JSON entity body. * \param[in] p_data Raw body bytes. * \param[out] p_json_body Decoded JSON body value. * \param[in] p_content_type Content-Type string. * \param[in] p_params Optional parameters. * \return \c true on success, \c false if unsupported. */ virtual bool decode_body_json(const OCTETSTRING &p_data, LibHttp__JsonMessageBodyTypes::JsonBody &p_json_body, const std::string& p_content_type, params* p_params) {return false;}; private: Loading
ccsrc/Protocols/Http/http_layer.cc +155 −29 File changed.Preview size limit exceeded, changes collapsed. Show changes
ccsrc/Protocols/Http/http_layer.hh +57 −0 Original line number Diff line number Diff line Loading @@ -10,7 +10,9 @@ */ #pragma once #include <map> #include <memory> #include <string> #include "t_layer.hh" Loading @@ -37,6 +39,61 @@ protected: //! \protectedsection bool _device_mode; public: //! \publicsection /*! * \brief Map of HTTP status code (as a string, e.g. \c "404") to its * standard reason phrase (e.g. \c "Not Found"), per RFC 7231 and the * IANA HTTP Status Code Registry. Used to fill in the reason phrase * of a status line when one was not otherwise provided. */ static inline const std::map<std::string, std::string> status_text = { {"100", "Continue"}, {"101", "Switching Protocols"}, {"200", "OK"}, {"201", "Created"}, {"202", "Accepted"}, {"203", "Non-Authoritative Information"}, {"204", "No Content"}, {"205", "Reset Content"}, {"206", "Partial Content"}, {"300", "Multiple Choices"}, {"301", "Moved Permanently"}, {"302", "Found"}, {"303", "See Other"}, {"304", "Not Modified"}, {"305", "Use Proxy"}, {"307", "Temporary Redirect"}, {"308", "Permanent Redirect"}, {"400", "Bad Request"}, {"401", "Unauthorized"}, {"402", "Payment Required"}, {"403", "Forbidden"}, {"404", "Not Found"}, {"405", "Method Not Allowed"}, {"406", "Not Acceptable"}, {"407", "Proxy Authentication Required"}, {"408", "Request Timeout"}, {"409", "Conflict"}, {"410", "Gone"}, {"411", "Length Required"}, {"412", "Precondition Failed"}, {"413", "Payload Too Large"}, {"414", "URI Too Long"}, {"415", "Unsupported Media Type"}, {"416", "Range Not Satisfiable"}, {"417", "Expectation Failed"}, {"426", "Upgrade Required"}, {"428", "Precondition Required"}, {"429", "Too Many Requests"}, {"431", "Request Header Fields Too Large"}, {"500", "Internal Server Error"}, {"501", "Not Implemented"}, {"502", "Bad Gateway"}, {"503", "Service Unavailable"}, {"504", "Gateway Timeout"}, {"505", "HTTP Version Not Supported"}, }; /*! * \brief Specialised constructor * Create a new instance of the http_layer class Loading
ccsrc/Protocols/Json/json_codec.hh +35 −0 Original line number Diff line number Diff line /*! * \file json_codec.hh * \brief Header file for the JSON HTTP message body codec. * \author ETSI STF685 * \copyright ETSI Copyright Notification * No part may be reproduced except as authorized by written permission. * The copyright and the foregoing restriction extend to reproduction in all media. * All rights reserved. * \version 0.1 */ #pragma once #include "codec_gen.hh" Loading @@ -11,13 +21,38 @@ namespace LibHttp__JsonMessageBodyTypes { class JsonBody; } /*! * \class json_codec * \brief Codec for JSON-encoded HTTP message bodies. * * Encodes a TITAN \c JsonBody value to a UTF-8 JSON octet string and decodes * a UTF-8 JSON octet string back to a \c JsonBody value. Registered with * \ref codec_stack_builder under the key \c "json_codec" and instantiated by * \c json_codec_factory when the layer stack descriptor includes that key. */ class json_codec: public codec_gen<LibHttp__JsonMessageBodyTypes::JsonBody, LibHttp__JsonMessageBodyTypes::JsonBody> { public: /*! \brief Default constructor. */ explicit json_codec() : codec_gen<LibHttp__JsonMessageBodyTypes::JsonBody, LibHttp__JsonMessageBodyTypes::JsonBody>() { }; /*! \brief Destructor. */ virtual ~json_codec() { }; /*! * \brief Encode a JSON message body to a UTF-8 octet string. * \param[in] p_body The JSON body value to encode. * \param[out] data The resulting UTF-8 JSON octet string. * \return 0 on success, -1 on failure. */ virtual int encode (const LibHttp__JsonMessageBodyTypes::JsonBody&, OCTETSTRING& data); /*! * \brief Decode a UTF-8 JSON octet string into a JSON message body. * \param[in] p_data The JSON octet string to decode. * \param[out] p_body The decoded JSON body value. * \param[in] p_params Optional decoding parameters (may be NULL). * \return 0 on success, -1 on failure. */ virtual int decode (const OCTETSTRING& p_data, LibHttp__JsonMessageBodyTypes::JsonBody&, params* p_params = NULL); }; // End of class json_codec