diff options
Diffstat (limited to 'web/apache/request')
-rw-r--r-- | web/apache/request | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/web/apache/request b/web/apache/request new file mode 100644 index 0000000..ab5c765 --- /dev/null +++ b/web/apache/request @@ -0,0 +1,228 @@ +// file : web/apache/request -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_APACHE_REQUEST +#define WEB_APACHE_REQUEST + +#include <stdexcept> +#include <exception> +#include <string> +#include <ios> +#include <istream> +#include <ostream> +#include <streambuf> +#include <memory> // unique_ptr +#include <algorithm> // move +#include <chrono> +#include <cassert> + +#include <apr_strings.h> + +#include <httpd/httpd.h> +#include <httpd/http_core.h> +#include <httpd/util_script.h> + +#include <web/module> +#include <web/apache/stream> + +namespace web +{ + namespace apache + { + class request : public web::request, public web::response + { + friend class service; + + request (request_rec* rec) noexcept: rec_ (rec) {} + + // Flush of buffered content. + // + int + flush (); + + // Get request body data stream. + // + virtual std::istream& + data () + { + if (write_flag ()) + { + throw sequence_error ("::web::apache::request::data"); + } + + if (!in_) + { + std::unique_ptr<std::streambuf> in_buf (new istreambuf (rec_)); + in_.reset (new std::istream (in_buf.get ())); + in_buf_ = std::move (in_buf); + in_->exceptions (std::ios::failbit | std::ios::badbit); + + // Save form data now otherwise will not be available to do later + // when data read from stream. + // + form_data (); + } + + return *in_; + } + + // Get request parameters. + // + virtual const name_values& + parameters () + { + if (!parameters_) + { + parameters_.reset (new name_values()); + + try + { + parse_parameters(rec_->args); + parse_parameters(form_data ()->c_str ()); + } + catch(const std::invalid_argument& ) + { + throw invalid_request(); + } + } + + return *parameters_; + } + + // Get request cookies. + // + virtual const name_values& + cookies (); + + // Get response status code. + // + status_code status () const noexcept {return status_;} + + // Set response status code. + // + virtual void + status (status_code status) + { + if (status != status_) + { + // Setting status code in exception handler is a common usecase + // where no sense to throw but still need to signal apache a + // proper status code. + // + if (write_flag () && !std::current_exception ()) + { + throw sequence_error ("::web::apache::request::status"); + } + + status_ = status; + type_.clear (); + buffer_ = true; + out_.reset (); + out_buf_.reset (); + set_content_type (); + } + } + + // 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 = 0, + const char* path = 0, + const char* domain = 0, + bool secure = false); + + private: + using string_ptr = std::unique_ptr<std::string>; + + // Get application/x-www-form-urlencoded form data. + // + const string_ptr& + form_data (); + + void + parse_parameters (const char* args); + + static void + mime_url_encode (const char* v, std::ostream& o); + + static std::string + mime_url_decode (const char* b, const char* e, bool trim = false); + + // Save content type to apache internals. + // + void + set_content_type () const noexcept + { + if (type_.empty ()) + ap_set_content_type (rec_, nullptr); + else + { + if(status_ == HTTP_OK) + { + ap_set_content_type (rec_, + apr_pstrdup (rec_->pool, type_.c_str ())); + } + else + { + // Unfortunatelly there is no way to set a proper content type + // for error custom response. Depending on presense of + // "suppress-error-charset" key in request_rec::subprocess_env + // table content type is set to "text/html" otherwise to + // "text/html; charset=iso-8859-1" (read http_protocol.c for + // details). I have chosen the first one as it is better not to + // specify charset than to set a wrong one. Ensure to put + // a proper encoding to + // <meta http-equiv="Content-Type" content="text/html;charset=..."> + // tag so browser can render the page properly. + // The clean solution would be patching apache but let's leave this + // troublesome option untill really required. + // + apr_table_set (rec_->subprocess_env, "suppress-error-charset", ""); + } + } + } + + bool + write_flag () const noexcept + { + if (!buffer_) + { + assert (out_buf_); + auto b = dynamic_cast<ostreambuf*> (out_buf_.get ()); + assert (b); + return b->write_flag (); + } + + return false; + } + + private: + + request_rec* rec_; + status_code status_ {HTTP_OK}; + std::string type_; + bool buffer_ {true}; + std::unique_ptr<std::streambuf> out_buf_; + std::unique_ptr<std::ostream> out_; + std::unique_ptr<std::streambuf> in_buf_; + std::unique_ptr<std::istream> in_; + std::unique_ptr<name_values> parameters_; + std::unique_ptr<name_values> cookies_; + string_ptr form_data_; + }; + } +} + +#include <web/apache/request.ixx> + +#endif // WEB_APACHE_REQUEST |