aboutsummaryrefslogtreecommitdiff
path: root/web/apache/request
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-04-23 12:43:52 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-04-23 12:43:52 +0200
commita20443c285dabdec8d2ee740500c62e31ad90c7b (patch)
treeb18db4007e45f8db1f97c0d5abf78729138406ac /web/apache/request
parent370e361db628f60bca5509dcc354014569d56752 (diff)
Implement apache service
Diffstat (limited to 'web/apache/request')
-rw-r--r--web/apache/request228
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