diff options
Diffstat (limited to 'web/apache/request.cxx')
-rw-r--r-- | web/apache/request.cxx | 224 |
1 files changed, 212 insertions, 12 deletions
diff --git a/web/apache/request.cxx b/web/apache/request.cxx index 3711437..caf0d60 100644 --- a/web/apache/request.cxx +++ b/web/apache/request.cxx @@ -25,6 +25,48 @@ namespace web { namespace apache { + + istream& request:: + content () + { + 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); + + // Save form data now otherwise will not be available to do later + // when data already read from stream. + // + form_data (); + } + + return *in_; + } + + const name_values& request:: + parameters () + { + if (!parameters_) + { + parameters_.reset (new name_values ()); + + try + { + parse_parameters (rec_->args); + parse_parameters (form_data ().c_str ()); + } + catch (const invalid_argument& ) + { + throw invalid_request (); + } + } + + return *parameters_; + } + const name_values& request:: cookies () { @@ -32,8 +74,8 @@ namespace web { cookies_.reset (new name_values ()); - const apr_array_header_t* ha = apr_table_elts (rec_->headers_in); - size_t n = ha->nelts; + const apr_array_header_t* ha (apr_table_elts (rec_->headers_in)); + size_t n (ha->nelts); for (auto h (reinterpret_cast<const apr_table_entry_t *> (ha->elts)); n--; ++h) @@ -42,8 +84,8 @@ namespace web { for (const char* n (h->val); n != 0; ) { - const char* v = strchr (n, '='); - const char* e = strchr (n, ';'); + const char* v (strchr (n, '=')); + const char* e (strchr (n, ';')); if (e && e < v) v = 0; @@ -97,12 +139,12 @@ namespace web // form_data (); - unique_ptr<std::streambuf> out_buf ( + unique_ptr<streambuf> out_buf ( buffer - ? static_cast<std::streambuf*> (new std::stringbuf ()) - : static_cast<std::streambuf*> (new ostreambuf (rec_, *this))); + ? static_cast<streambuf*> (new stringbuf ()) + : static_cast<streambuf*> (new ostreambuf (rec_, *this))); - out_.reset (new std::ostream (out_buf.get ())); + out_.reset (new ostream (out_buf.get ())); out_buf_ = move (out_buf); out_->exceptions (ios::eofbit | ios::failbit | ios::badbit); @@ -117,6 +159,28 @@ namespace web } void request:: + status (status_code status) + { + if (status != rec_->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 (get_write_state () && !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); + } + } + + void request:: cookie (const char* name, const char* value, const chrono::seconds* max_age, @@ -126,6 +190,8 @@ namespace web { if (get_write_state ()) { + // Too late to send cookie if content is already written. + // throw sequence_error ("::web::apache::request::cookie"); } @@ -136,12 +202,12 @@ namespace web if (max_age) { - chrono::system_clock::time_point tp = - chrono::system_clock::now () + *max_age; + chrono::system_clock::time_point tp ( + chrono::system_clock::now () + *max_age); - time_t t = chrono::system_clock::to_time_t (tp); + time_t t (chrono::system_clock::to_time_t (tp)); - // Assume global "C" locale is not changed. + // Assume global locale is not changed and still "C". // char b[100]; strftime (b, sizeof (b), "%a, %d-%b-%Y %H:%M:%S GMT", gmtime (&t)); @@ -166,5 +232,139 @@ namespace web apr_table_add (rec_->err_headers_out, "Set-Cookie", s.str ().c_str ()); } + void request:: + parse_parameters (const char* args) + { + for (auto n (args); n != 0; ) + { + const char* v (strchr (n, '=')); + const char* e (strchr (n, '&')); + + if (e && e < v) + v = 0; + + string name (v + ? mime_url_decode (n, v) : + (e + ? mime_url_decode (n, e) + : mime_url_decode (n, n + strlen (n)))); + + string value; + + if (v++) + { + value = e + ? mime_url_decode (v, e) + : mime_url_decode (v, v + strlen (v)); + } + + if (!name.empty () || !value.empty ()) + parameters_->emplace_back (move (name), move (value)); + + n = e ? e + 1 : 0; + } + } + + void request:: + mime_url_encode (const char* v, ostream& o) + { + char f (o.fill ()); + ios_base::fmtflags g (o.flags ()); + o << hex << uppercase << right << setfill ('0'); + + char c; + + while ((c = *v++) != '\0') + { + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9')) + { + o << c; + } + else + switch (c) + { + case ' ': o << '+'; break; + case '.': + case '_': + case '-': + case '~': o << c; break; + default: + { + o << "%" << setw (2) << static_cast<unsigned short> (c); + break; + } + } + } + + o.flags (g); + o.fill (f); + } + + string request:: + mime_url_decode (const char* b, const char* e, bool trim) + { + if (trim) + { + b += strspn (b, " "); + + if (b >= e) + return string (); + + while (*--e == ' '); + ++e; + } + + string value; + value.reserve (e - b); + + char bf[3]; + bf[2] = '\0'; + + while (b != e) + { + char c (*b++); + + switch (c) + { + case '+': + { + value.append (" "); + break; + } + case '%': + { + if (*b == '\0' || b[1] == '\0') + { + throw invalid_argument ( + "::web::apache::request::mime_url_decode short"); + } + + *bf = *b; + bf[1] = b[1]; + + char* ebf (nullptr); + size_t vl (strtoul (bf, &ebf, 16)); + + if (*ebf != '\0') + { + throw invalid_argument ( + "::web::apache::request::mime_url_decode wrong"); + } + + value.append (1, static_cast<char> (vl)); + b += 2; + break; + } + default: + { + value.append (1, c); + break; + } + } + } + + return value; + } } } |