diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-04-23 12:43:52 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-04-23 12:43:52 +0200 |
commit | a20443c285dabdec8d2ee740500c62e31ad90c7b (patch) | |
tree | b18db4007e45f8db1f97c0d5abf78729138406ac /web/apache/stream | |
parent | 370e361db628f60bca5509dcc354014569d56752 (diff) |
Implement apache service
Diffstat (limited to 'web/apache/stream')
-rw-r--r-- | web/apache/stream | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/web/apache/stream b/web/apache/stream new file mode 100644 index 0000000..eb62b85 --- /dev/null +++ b/web/apache/stream @@ -0,0 +1,161 @@ +// file : web/apache/stream -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_APACHE_STREAM +#define WEB_APACHE_STREAM + +#include <streambuf> +#include <ios> // streamsize +#include <algorithm> // min(), max() +#include <cstring> // memmove() +#include <memory> // unique_ptr + +#include <httpd/httpd.h> +#include <httpd/http_protocol.h> + +#include <web/module> + +namespace web +{ + namespace apache + { + class ostreambuf : public std::streambuf + { + public: + ostreambuf (request_rec* rec) : rec_ (rec) {} + + bool + write_flag () const noexcept {return write_;} + + private: + virtual int_type + overflow (int_type c) + { + if (c != traits_type::eof ()) + { + flag_write (); + + char chr = c; + + // Throwing allows to distinguish comm failure from other IO error + // conditions. + // + if (ap_rwrite (&chr, sizeof (chr), rec_) == -1) + throw invalid_request (HTTP_REQUEST_TIME_OUT); + } + + return c; + } + + virtual std::streamsize + xsputn (const char* s, std::streamsize num) + { + flag_write (); + + if (ap_rwrite (s, num, rec_) < 0) + { + throw invalid_request (HTTP_REQUEST_TIME_OUT); + } + + return num; + } + + virtual int + sync () + { + if(ap_rflush (rec_) < 0) + { + throw invalid_request (HTTP_REQUEST_TIME_OUT); + } + + return 0; + } + + void + flag_write () noexcept + { + if (!write_) + { + // Preparing to write a response read and discard request + // body if any. + // + int r = ap_discard_request_body (rec_); + + if (r != OK) + { + throw invalid_request (r); + } + + write_ = true; + } + } + + private: + + request_rec* rec_; + bool write_ {false}; + }; + + class istreambuf : public std::streambuf + { + public: + istreambuf (request_rec* rec, size_t bufsize = 1024, size_t putback = 1) + : rec_ (rec), + bufsize_ (std::max (bufsize, (size_t)1)), + putback_ (std::min (putback, bufsize_ - 1)), + buf_ (new char[bufsize_]) + { + char* p = buf_.get () + putback_; + setg (p, p, p); + + int status = ap_setup_client_block (rec_, REQUEST_CHUNKED_DECHUNK); + + if (status != OK) + { + throw invalid_request (status); + } + } + + private: + virtual int_type + underflow () + { + if (gptr () < egptr ()) + return traits_type::to_int_type (*gptr ()); + + size_t pb = std::min ((size_t)(gptr () - eback ()), putback_); + std::memmove (buf_.get () + putback_ - pb, gptr () - pb, pb); + + char* p = buf_.get () + putback_; + int rb = ap_get_client_block (rec_, p, bufsize_ - putback_); + + if (rb == 0) + { + return traits_type::eof (); + } + + if (rb < 0) + { + throw invalid_request (HTTP_REQUEST_TIME_OUT); + } + + setg (p - pb, p, p + rb); + return traits_type::to_int_type (*gptr ()); + } + + bool error () const noexcept {return error_;} + + private: + + request_rec* rec_; + size_t bufsize_; + size_t putback_; + std::unique_ptr<char[]> buf_; + bool error_ {false}; + }; + + } +} + +#endif // WEB_APACHE_STREAM |