aboutsummaryrefslogtreecommitdiff
path: root/web/apache/request.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'web/apache/request.cxx')
-rw-r--r--web/apache/request.cxx147
1 files changed, 115 insertions, 32 deletions
diff --git a/web/apache/request.cxx b/web/apache/request.cxx
index 2e03190..b0b4d61 100644
--- a/web/apache/request.cxx
+++ b/web/apache/request.cxx
@@ -4,22 +4,30 @@
#include <web/apache/request>
-#include <apr_tables.h>
+#include <apr_tables.h> // apr_table_*, apr_array_header_t
+#include <apr_strings.h> // apr_pstrdup()
+
+#include <httpd.h> // request_rec, HTTP_*, OK
+#include <http_protocol.h> // ap_*()
#include <strings.h> // strcasecmp()
-#include <ios>
-#include <ctime>
+#include <ctime> // strftime(), time_t
#include <chrono>
#include <memory> // unique_ptr
+#include <string>
+#include <cassert>
#include <sstream>
#include <ostream>
#include <istream>
-#include <cstring>
+#include <cstring> // str*(), size_t
#include <utility> // move()
-#include <stdexcept>
+#include <stdexcept> // invalid_argument
+#include <exception> // current_exception()
#include <streambuf>
+#include <butl/optional>
+
#include <web/mime-url-encoding>
using namespace std;
@@ -28,16 +36,92 @@ namespace web
{
namespace apache
{
+ void request::
+ state (request_state s)
+ {
+ assert (s != request_state::initial);
+
+ if (s == state_)
+ return; // Noop.
+
+ if (s < state_)
+ {
+ // Can't "unwind" irrevocable interaction with Apache API.
+ //
+ static const char* names[] = {
+ "initial", "reading", "headers", "writing"};
+
+ string str ("web::apache::request::set_state: ");
+ str += names[static_cast<size_t> (state_)];
+ str += " to ";
+ str += names[static_cast<size_t> (s)];
+
+ throw sequence_error (move (str));
+ }
+
+ if (s == request_state::reading)
+ {
+ // Prepare request content for reading.
+ //
+ int r (ap_setup_client_block (rec_, REQUEST_CHUNKED_DECHUNK));
+
+ if (r != OK)
+ throw invalid_request (r);
+ }
+ else if (s > request_state::reading && state_ <= request_state::reading)
+ {
+ // Read request content if any, discard whatever is received.
+ //
+ int r (ap_discard_request_body (rec_));
+
+ if (r != OK)
+ throw invalid_request (r);
+ }
+
+ state_ = s;
+ }
+
+ void request::
+ rewind ()
+ {
+ // @@ Request content buffering, and response cookies buffering are not
+ // supported yet. When done will be possible to rewind in broader
+ // range of cases.
+ //
+
+ if (state_ == request_state::initial ||
+
+ // Form data have been read. Lucky case, can rewind.
+ //
+ (state_ == request_state::reading &&
+ dynamic_cast<stringbuf*> (in_buf_.get ()) != nullptr))
+ {
+ out_.reset ();
+ out_buf_.reset ();
+
+ rec_->status = HTTP_OK;
+
+ ap_set_content_type (rec_, nullptr);
+
+ if (in_)
+ in_->seekg (0);
+ }
+ else
+ throw sequence_error ("web::apache::request::rewind");
+ }
+
istream& request::
- content ()
+ content (bool buffer)
{
+ assert (!buffer); // Request content buffering is not implemented yet.
+
if (!in_)
{
unique_ptr<streambuf> in_buf (new istreambuf (rec_, *this));
in_.reset (new istream (in_buf.get ()));
in_buf_ = move (in_buf);
- in_->exceptions (ios::failbit | ios::badbit);
+ in_->exceptions (istream::failbit | istream::badbit);
// Save form data now otherwise will not be available to do later
// when data already read from stream.
@@ -135,17 +219,29 @@ namespace web
ostream& request::
content (status_code status, const string& type, bool buffer)
{
- if (out_ && status == rec_->status && buffer == buffer_ &&
+ if (out_ &&
+
+ // Same status code.
+ //
+ status == rec_->status &&
+
+ // Same buffering flag.
+ //
+ buffer ==
+ (dynamic_cast<stringbuf*> (out_buf_.get ()) != nullptr) &&
+
+ // Same content type.
+ //
strcasecmp (rec_->content_type ? rec_->content_type : "",
type.c_str ()) == 0)
{
+ // No change, return the existing stream.
+ //
return *out_;
}
- if (get_write_state ())
- {
- throw sequence_error ("::web::apache::request::content");
- }
+ if (state_ >= request_state::writing)
+ throw sequence_error ("web::apache::request::content");
if (!buffer)
// Request body will be discarded prior first byte of content is
@@ -161,9 +257,8 @@ namespace web
out_.reset (new ostream (out_buf.get ()));
out_buf_ = move (out_buf);
- out_->exceptions (ios::eofbit | ios::failbit | ios::badbit);
+ out_->exceptions (ostream::eofbit | ostream::failbit | ostream::badbit);
- buffer_ = buffer;
rec_->status = status;
ap_set_content_type (
@@ -182,13 +277,10 @@ namespace web
// where no sense to throw but still need to signal apache a
// proper status code.
//
- if (get_write_state () && !current_exception ())
- {
- throw sequence_error ("::web::apache::request::status");
- }
+ if (state_ >= request_state::writing && !current_exception ())
+ throw sequence_error ("web::apache::request::status");
rec_->status = status;
- buffer_ = true;
out_.reset ();
out_buf_.reset ();
ap_set_content_type (rec_, nullptr);
@@ -201,14 +293,10 @@ namespace web
const chrono::seconds* max_age,
const char* path,
const char* domain,
- bool secure)
+ bool secure,
+ bool buffer)
{
- if (get_write_state ())
- {
- // Too late to send cookie if content is already written.
- //
- throw sequence_error ("::web::apache::request::cookie");
- }
+ assert (!buffer); // Cookie buffering is not implemented yet.
ostringstream s;
mime_url_encode (name, s);
@@ -230,20 +318,15 @@ namespace web
}
if (path)
- {
s << ";Path=" << path;
- }
if (domain)
- {
s << ";Domain=" << domain;
- }
if (secure)
- {
s << ";Secure";
- }
+ state (request_state::headers);
apr_table_add (rec_->err_headers_out, "Set-Cookie", s.str ().c_str ());
}