// 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 #include #include #include #include #include #include #include // unique_ptr #include // move #include #include #include #include #include #include #include #include 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 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; // 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 // // 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 (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 out_buf_; std::unique_ptr out_; std::unique_ptr in_buf_; std::unique_ptr in_; std::unique_ptr parameters_; std::unique_ptr cookies_; string_ptr form_data_; }; } } #include #endif // WEB_APACHE_REQUEST