diff options
Diffstat (limited to 'web/server/apache/request.hxx')
-rw-r--r-- | web/server/apache/request.hxx | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/web/server/apache/request.hxx b/web/server/apache/request.hxx new file mode 100644 index 0000000..bc105ec --- /dev/null +++ b/web/server/apache/request.hxx @@ -0,0 +1,233 @@ +// file : web/server/apache/request.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_SERVER_APACHE_REQUEST_HXX +#define WEB_SERVER_APACHE_REQUEST_HXX + +#include <httpd.h> // request_rec, HTTP_*, OK, M_POST + +#include <chrono> +#include <memory> // unique_ptr +#include <string> +#include <vector> +#include <istream> +#include <ostream> +#include <streambuf> + +#include <web/server/module.hxx> +#include <web/server/apache/stream.hxx> + +namespace web +{ + namespace apache + { + // The state of the request processing, reflecting an interaction with + // Apache API (like reading/writing content function calls), with no + // buffering taken into account. Any state different from the initial + // suppose that some irrevocable interaction with Apache API have + // happened, so request processing should be either completed, or + // reported as failed. State values are ordered in a sense that the + // higher value reflects the more advanced stage of processing, so the + // request current state value may not decrease. + // + enum class request_state + { + // Denotes the initial stage of the request handling. At this stage + // the request line and headers are already parsed by Apache. + // + initial, + + // Reading the request content. + // + reading, + + // Adding the response headers (cookies in particular). + // + headers, + + // Writing the response content. + // + writing + }; + + // Extends istreambuf with read limit checking, caching, etc. (see the + // implementation for details). + // + class istreambuf_cache; + + // Stream type for reading from Apache's bucket brigades. + // + class istream_buckets; + + class request: public web::request, + public web::response, + public stream_state + { + friend class service; + + // Can not be inline/default due to the member of + // unique_ptr<istreambuf_cache> type. Note that istreambuf_cache type is + // incomplete. + // + request (request_rec* rec) noexcept; + ~request (); + + request_state + state () const noexcept {return state_;} + + // Flush the buffered response content if present. The returned value + // should be passed to Apache API on request handler exit. + // + int + flush (); + + // Prepare for the request re-processing if possible (no unbuffered + // read/write operations have been done). Throw sequence_error + // otherwise. In particular, the preparation can include the response + // content buffer cleanup, the request content buffer rewind. + // + void + rewind (); + + // Get request path. + // + virtual const path_type& + path (); + + // Get request body data stream. + // + virtual std::istream& + content (std::size_t limit = 0, std::size_t buffer = 0); + + // Get request parameters. + // + virtual const name_values& + parameters (std::size_t limit, bool url_only = false); + + // Get upload stream. + // + virtual std::istream& + open_upload (std::size_t index); + + virtual std::istream& + open_upload (const std::string& name); + + // Get request headers. + // + virtual const name_values& + headers (); + + // Get request cookies. + // + virtual const name_values& + cookies (); + + // Get response status code. + // + status_code + status () const noexcept {return rec_->status;} + + // Set response status code. + // + virtual void + status (status_code status); + + // Set response status code, content type and get body stream. + // + virtual std::ostream& + content (status_code status, + const std::string& type, + bool buffer = true); + + // Add response cookie. + // + virtual void + cookie (const char* name, + const char* value, + const std::chrono::seconds* max_age = nullptr, + const char* path = nullptr, + const char* domain = nullptr, + bool secure = false, + bool buffer = true); + + private: + // On the first call cache the application/x-www-form-urlencoded or + // multipart/form-data form data for the subsequent parameters parsing + // and set the multipart flag accordingly. Don't cache if the request is + // in the reading or later state. Return true if the cache contains the + // form data. + // + // Note that the function doesn't change the content buffering (see + // content() function for details) nor rewind the content stream after + // reading. + // + bool + form_data (std::size_t limit); + + // Used to also parse application/x-www-form-urlencoded POST body. + // + void + parse_url_parameters (const char* args); + + void + parse_multipart_parameters (const std::vector<char>& body); + + // Return a list of the upload input streams. Throw sequence_error if + // the parameters() function was not called yet. Throw invalid_argument + // if the request doesn't contain multipart form data. + // + using uploads_type = std::vector<std::unique_ptr<istream_buckets>>; + + uploads_type& + uploads () const; + + // Advance the request processing state. Noop if new state is equal to + // the current one. Throw sequence_error if the new state is less then + // the current one. Can throw invalid_request if HTTP request is + // malformed. + // + void + state (request_state); + + // stream_state members implementation. + // + virtual void + set_read_state () {state (request_state::reading);} + + virtual void + set_write_state () {state (request_state::writing);} + + private: + request_rec* rec_; + request_state state_ = request_state::initial; + + path_type path_; + + std::unique_ptr<name_values> parameters_; + bool url_only_parameters_; // Meaningless if parameters_ is NULL; + + // Uploaded file streams. If not NULL, is parallel to the parameters + // list. + // + std::unique_ptr<uploads_type> uploads_; + + std::unique_ptr<name_values> headers_; + std::unique_ptr<name_values> cookies_; + + // Form data cache. Is empty if the body doesn't contain the form data. + // + std::unique_ptr<std::vector<char>> form_data_; + bool form_multipart_; // Meaningless if form_data_ is NULL or empty; + + std::unique_ptr<istreambuf_cache> in_buf_; + std::unique_ptr<std::istream> in_; + + std::unique_ptr<std::streambuf> out_buf_; + std::unique_ptr<std::ostream> out_; + }; + } +} + +#include <web/server/apache/request.ixx> + +#endif // WEB_SERVER_APACHE_REQUEST_HXX |