|
Boost-Commit : |
From: lists.drrngrvy_at_[hidden]
Date: 2007-08-10 22:15:25
Author: drrngrvy
Date: 2007-08-10 22:15:23 EDT (Fri, 10 Aug 2007)
New Revision: 38589
URL: http://svn.boost.org/trac/boost/changeset/38589
Log:
Code documenting and an example:
* Documented basic_protocol_service.hpp;
* Documented and cleaned up basic_request.hpp;
* Added fcgi_threadpool_server to examples (can be generalized)
Added:
sandbox/SOC/2007/cgi/libs/cgi/example/servers/
sandbox/SOC/2007/cgi/libs/cgi/example/servers/fcgi_threadpool_server.cpp (contents, props changed)
Text files modified:
sandbox/SOC/2007/cgi/boost/cgi/basic_protocol_service.hpp | 86 ++++++++----
sandbox/SOC/2007/cgi/boost/cgi/basic_request.hpp | 271 +++++++++++++++++++--------------------
2 files changed, 189 insertions(+), 168 deletions(-)
Modified: sandbox/SOC/2007/cgi/boost/cgi/basic_protocol_service.hpp
==============================================================================
--- sandbox/SOC/2007/cgi/boost/cgi/basic_protocol_service.hpp (original)
+++ sandbox/SOC/2007/cgi/boost/cgi/basic_protocol_service.hpp 2007-08-10 22:15:23 EDT (Fri, 10 Aug 2007)
@@ -13,12 +13,14 @@
#include <queue>
#include <boost/shared_ptr.hpp>
#include <boost/asio/io_service.hpp>
+#include <boost/asio/strand.hpp>
#include "io_service_provider.hpp"
-#include "basic_gateway.hpp"
+//#include "basic_gateway.hpp"
//#include "basic_connection_fwd.hpp"
-#include "basic_acceptor_fwd.hpp"
+//#include "basic_acceptor_fwd.hpp"
#include "basic_request_fwd.hpp"
+#include "detail/protocol_traits.hpp"
namespace cgi {
@@ -27,53 +29,82 @@
* Holds the request queue and the connection queue.
* It is also a wrapper around asio::io_service
*/
- template<typename Protocol, int IoServiceCount, typename PoolingPolicy>
+ template<typename Protocol, typename IoServiceProvider>
class basic_protocol_service
//: public protocol_traits<Protocol> // do this!
{
public:
- typedef Protocol protocol_type;
- typedef io_service_provider<IoServiceCount, PoolingPolicy>
- ios_provider_type;
- //typedef protocol_traits<Protocol> traits;
- //typedef typename traits::gateway_type gateway_type;
- typedef basic_gateway<Protocol> gateway_type;
- typedef basic_request<Protocol> request_type;
- typedef boost::shared_ptr<request_type> request_ptr;
- typedef basic_acceptor<Protocol> acceptor_type;
+ typedef Protocol protocol_type;
+ typedef IoServiceProvider ios_provider_type;
+ typedef typename detail::protocol_traits<Protocol>::type traits;
+ typedef typename traits::request_type request_type;
+ //typedef typename traits::gateway_type gateway_type;
+ //typedef basic_gateway<Protocol> gateway_type;
+ //typedef basic_request<Protocol> request_type;
+ typedef typename boost::shared_ptr<request_type> request_ptr;
+ //typedef basic_acceptor<Protocol> acceptor_type;
- basic_protocol_service(int pool_size_hint = 0)
+ basic_protocol_service(int pool_size_hint = 1)
: ios_provider_(pool_size_hint)
-// , mutex_()
- , gateway_(*this)
+ //, strand_(ios_provider_.io_service())
+ //, gateway_(*this)
{
}
- basic_protocol_service(boost::asio::io_service& io_service)
- : ios_provider_(io_service)
-// , mutex_()
- , gateway_(*this)
+ basic_protocol_service(boost::asio::io_service& ios)
+ : ios_provider_(ios)
+ //, strand_(ios)
+ //, gateway_(*this)
{
}
~basic_protocol_service()
{
- gateway_.stop();
+ //gateway_.stop();
}
+ /// Run all the io_services contained by this service
+ /**
+ * This is equivalent to calling run() on each of the io_services held by
+ * ios_provider_
+ */
void run()
{
ios_provider_.run();
}
+ /// Stop all the io_services contained by this service
+ /**
+ * This is equivalent to calling stop() on each of the io_services held by
+ * ios_provider_
+ */
void stop()
{
- gateway_.stop();
+ //gateway_.stop();
ios_provider_.stop();
}
+ /// Reset all the io_services contained by this service
+ /**
+ * This deletes the request queue(s), aborts all running requests and then
+ * calls reset() on each of the io_services held by ios_provider_. There is
+ * no guarantee that requests will terminate immediately.
+ */
+ void reset()
+ {
+ request_queue_.clear();
+ std::for_each(request_set_.begin(), request_set_.end()
+ , boost::bind(&request_type::abort, boost::ref(*_1)));
+ request_set_.clear();
+ ios_provider_.reset();
+ }
+
/// Return an available io_service from the IoServiceProvider
+ /**
+ * The order in which the underlying io_services are returned is determined
+ * by what policy the IoServiceProvider uses.
+ */
boost::asio::io_service& io_service()
{
return ios_provider_.io_service();
@@ -95,20 +126,19 @@
private:
ios_provider_type ios_provider_;
-// boost::thread::mutex mutex_;
-// boost::thread::condition condition_;
/// A strand is used for guaranteeing handlers are dispatched sequentially
-// boost::asio::strand strand_;
+ //boost::asio::strand strand_;
+
std::set<request_ptr> request_set_;
std::queue<request_ptr> request_queue_;
- gateway_type gateway_;
+ //gateway_type gateway_;
- friend class basic_gateway<protocol_type>;//gateway_type;
- friend class basic_acceptor<protocol_type>;//class acceptor_type;
- friend class basic_request<protocol_type>;//typename request_type;
+ //friend class basic_gateway<protocol_type>;//gateway_type;
+ //friend class basic_acceptor<protocol_type>;//class acceptor_type;
+ friend class traits::request_type;//typename request_type;
};
} // namespace cgi
Modified: sandbox/SOC/2007/cgi/boost/cgi/basic_request.hpp
==============================================================================
--- sandbox/SOC/2007/cgi/boost/cgi/basic_request.hpp (original)
+++ sandbox/SOC/2007/cgi/boost/cgi/basic_request.hpp 2007-08-10 22:15:23 EDT (Fri, 10 Aug 2007)
@@ -37,15 +37,6 @@
namespace cgi {
- //using boost::asio::basic_io_object;
- /*
- enum status_type
- { ok
- , aborted
- , ended
- };
- */
-
/// The basic_request class, primary entry point to the library
/**
* Note: This class is supposed to make simple use of the library easy.
@@ -70,8 +61,7 @@
* to use the acceptor and this class doesn't seem worth it: it makes the
* whole library much simpler to do it this way.
*/
- template<typename Protocol
- , typename Service
+ template<typename RequestService
, role_type Role
, typename ProtocolService
, typename Allocator>
@@ -83,17 +73,13 @@
>::type
{
public:
- typedef basic_request<Protocol, Service, Role
+ typedef basic_request<RequestService, Role
, ProtocolService, Allocator > type;
- typedef Protocol protocol_type;
- //typedef Role role_type;
+ typedef RequestService service_type;
+ typedef service_type::protocol_type protocol_type;
typedef ProtocolService protocol_service_type;
typedef boost::shared_ptr<type> pointer;
- //typedef Service service_type;
- //typedef typename Service::impl_type impl_type;
- //typedef boost::shared_ptr<connection_base> connection_ptr;
-
// Throws
basic_request(bool load_now = true, bool parse_post = true)
@@ -127,12 +113,15 @@
~basic_request()
{
+ if (is_open())
+ close(http::internal_server_error, 0);
}
- //boost::asio::io_service& io_service()
- //{
- // return this->service.io_service(this->impl);
- //}
+ /// Return `true` if the request is still open (ie. not aborted or closed)
+ bool is_open()
+ {
+ return this->service.is_open(this->impl);
+ }
/// Synchronously read/parse the request meta-data
/**
@@ -174,40 +163,49 @@
* state of the request after it was finished handling. It is
* implementation defined how the server deals with this, and it may have
* no effect on the http status code returned to the client (eg. 200 OK).
+ *
+ * @returns The value of program_status
*/
- void close(http::status_code http_status, int program_status)
+ int close(http::status_code http_status, int program_status)
{
//BOOST_ASSERT( request_status_ != status_type::ended );
- this->service.set_status(this->impl, http_status);
- this->service.end(this->impl, http_status);
- }
-
- void close(int http_status, int program_status)
- {
- this->service.close(this->impl, http_status, program_status);
+ //this->service.set_status(this->impl, http_status);
+ return this->service.end(this->impl, http_status, program_status);
}
/// Reject the request with a standard '500 Internal Server Error' error
- void reject()
+ int reject()
{
this->service.set_status(this->impl, aborted);
- this->service.end(this->impl, http::internal_server_error);
+ return this->service.end(this->impl, http::internal_server_error);
}
- void set_source(cgi::sink dest = stdout_)
+ /// Set the output for the request
+ /**
+ * Not Implemented Yet ******************
+ *
+ * Set the output sink as `stdout_`, `stderr_`, or `stdout_ | stderr_`
+ */
+ void set_output(cgi::sink dest = stdout_)
{
boost::system::error_code ec;
this->service(this->impl, dest, ec);
detail::throw_error(ec);
}
- void set_source(cgi::sink dest, boost::system::error_code& ec)
+ /// Set the output for the request
+ /**
+ * Not Implemented Yet ******************
+ *
+ * Set the output sink as `stdout_`, `stderr_`, or `stdout_ | stderr_`
+ */
+ void set_output(cgi::sink dest, boost::system::error_code& ec)
{
this->service(this->impl, dest, ec);
}
- /// Read some data from the request
+ /// Read some data from the client
template<typename MutableBufferSequence/*, typename Source*/>
std::size_t read_some(const MutableBufferSequence& buf)
{
@@ -217,6 +215,7 @@
return s;
}
+ /// Read some data from the client
template<typename MutableBufferSequence/*, typename Source*/>
std::size_t read_some(const MutableBufferSequence& buf
, boost::system::error_code& ec)
@@ -224,6 +223,7 @@
return this->service.read_some(this->impl, buf, ec);
}
+ /// Write some data to the client
template<typename ConstBufferSequence/*, typename Sink*/>
std::size_t write_some(const ConstBufferSequence& buf)
{
@@ -233,6 +233,7 @@
return s;
}
+ /// Write some data to the client
template<typename ConstBufferSequence/*, typename Sink*/>
std::size_t write_some(const ConstBufferSequence& buf
, boost::system::error_code& ec)
@@ -240,29 +241,26 @@
return this->service.write_some(this->impl, buf, ec);
}
-/* Not sure if these are needed; leaving it for open discussion to decide.
-
- template<class VarType = ENV>
- const std::string& var(const std::string& name) const
- {
- return this->impl.var<VarType>(name);
- }
-
- template<typename ToType, class VarType = ENV>
- const std::string& var_as(const std::string& name) const
- {
- return boost::lexical_cast<ToType>(var<VarType>(name));
- }
-**/
-
/// Find the get meta-variable matching name
+ /**
+ * @throws `boost::system::system_error` if an error occurred. This may
+ * fail with `cgi::error::request_aborted` if the request has been aborted
+ * by the client.
+ */
std::string meta_get(const std::string& name)
{
boost::system::error_code ec;
- return this->service.meta_get(this->impl, name, ec);
+ std::string ret = this->service.meta_get(this->impl, name, ec);
detail::throw_error(ec);
+ return ret;
}
+ /// Find the get meta-variable matching name
+ /**
+ * @param ec Set such that `(!ec == false)` if an error occurred. This may
+ * fail with `ec == cgi::error::request_aborted` if the request has been
+ * aborted by the client.
+ */
std::string meta_get(const std::string& name, boost::system::error_code& ec)
{
return this->service.meta_get(this->impl, name, ec);
@@ -273,30 +271,106 @@
* @param greedy This determines whether more data can be read to find
* the variable. The default is true to cause least surprise in the common
* case of having not parsed any of the POST data.
+ *
+ * @throws `boost::system::system_error` if an error occurred. This may
+ * fail with `cgi::error::request_aborted` if the request has been aborted
+ * by the client.
*/
std::string meta_post(const std::string& name, bool greedy = true)
{
boost::system::error_code ec;
- return this->service.meta_post(this->impl, name, ec, greedy);
+ std::string ret = this->service.meta_post(this->impl, name, ec, greedy);
detail::throw_error(ec);
+ return ret;
}
+ /**
+ * @param ec Set such that `(!ec == false)` if an error occurred. This may
+ * fail with `ec == cgi::error::request_aborted` if the request has been
+ * aborted by the client.
+ */
std::string meta_post(const std::string& name, boost::system::error_code& ec
, bool greedy = true)
{
return this->service.meta_post(this->impl, name, ec, greedy);
}
+ /// Find the form variable matching name
+ /**
+ * Depending on the request's request_method, either the GET or the POST
+ * meta-variables are searched.
+ *
+ * @throws `boost::system::system_error` if an error occurred. This may
+ * fail with `cgi::error::request_aborted` if the request has been aborted
+ * by the client.
+ */
+ std::string meta_form(const std::string& name, bool greedy = true)
+ {
+ boost::system::error_code ec;
+ std::string ret = this->service.meta_form(this->impl, name, ec, greedy);
+ detail::throw_error(ec);
+ return ret;
+ }
+
+ /**
+ * @param ec Set such that `(!ec == false)` if an error occurred. This may
+ * fail with `ec == cgi::error::request_aborted` if the request has been
+ * aborted by the client.
+ */
+ std::string meta_form(const std::string& name, boost::system::error_code& ec
+ , bool greedy = true)
+ {
+ return this->service.meta_form(this->impl, name, ec, greedy);
+ }
+
/// Find the cookie meta-variable matching name
+ /**
+ * @throws `boost::system::system_error` if an error occurred. This may
+ * fail with `cgi::error::request_aborted` if the request has been aborted
+ * by the client.
+ */
std::string cookie(const std::string& name)
{
- return this->service.cookie(this->impl, name);
+ boost::system::error_code ec;
+ std::string ret = this->service.cookie(this->impl, name, ec);
+ detail::throw_error(ec);
+ return ret;
+ }
+
+ /// Find the cookie meta-variable matching name
+ /**
+ * @param ec Set such that `(!ec == false)` if an error occurred. This may
+ * fail with `ec == cgi::error::request_aborted` if the request has been
+ * aborted by the client.
+ */
+ std::string cookie(const std::string& name, boost::system::error_code& ec)
+ {
+ return this->service.cookie(this->impl, name, ec);
}
/// Find the environment meta-variable matching name
+ /**
+ * @throws `boost::system::system_error` if an error occurred. This may
+ * fail with `cgi::error::request_aborted` if the request has been aborted
+ * by the client.
+ */
std::string meta_env(const std::string& name)
{
- return this->service.meta_env(this->impl, name);
+ boost::system::error_code ec;
+ std::string ret = this->service.meta_env(this->impl, name, ec);
+ detail::throw_error(ec);
+ return ret;
+ }
+
+ /// Find the environment meta-variable matching name
+ /**
+ * @param ec Set such that `(!ec == false)` if an error occurred. This may
+ * fail with `ec == cgi::error::request_aborted` if the request has been
+ * aborted by the client.
+ */
+ std::string meta_env(const std::string& name, boost::system::error_code& ec)
+ {
+ return this->service.meta_env(this->impl, name, ec);
}
/// Search through all meta vars for the meta-variable matching name
@@ -315,12 +389,14 @@
*/
std::string meta_var(const std::string& name, bool greedy = false)
{
+ boost::system::error_code ec;
+ std::string ret = meta_var(name, ec, greedy);
return this->service.meta_var(this->impl, name, greedy);
std::string request_method( meta_env("REQUEST_METHOD") );
std::string tmp;
- // If it's not a POST request search meta_get first
+ // If it's not a POST request search meta_get first (to save time)
if (request_method.empty() || request_method == "GET")
{
tmp = meta_get(name);
@@ -451,93 +527,8 @@
{
return this->service.set_cookie(this->impl, name, value);
}
-
- private:
- //connection_ptr conn_;
- //service_type& service;
- //impl_type impl_;
-
- //int app_status_; // what to return to the server on request completion
- //http::status_code http_status_;
- //status_type request_status_;
-
- //boost::shared_ptr<data_type> data_;
-
- /// The data for the request is held in its own struct
- //data_type* data_;
-
};
-
-
-
- //template<>
- //class basic_request<protocol::cgi>::meta_data
- //{
- //public:
- // meta_data()
- // : bytes_left_(-1)
- // {}
-
- // std::string get_var(const std::string& name)
- // {
- // if( get_vars_.find(name) != get_vars_.end() )
- // return get_vars_[name];
- // return "";
- // }
- // std::string post_var(const std::string& name)
- // {
- // assert( getenv("REQUEST_METHOD") == "POST" );
-
- // // see if we already have the value stored
- // if( post_vars_.find(name) != post_vars_.end() )
- // return post_vars_[name];
- // // else check if there's more data to read
- // while( !read_all_data() )
- // {
- // switch( read_var(name) )
- // {
- // case true:
- // return post_vars_[name];
- // case false:
- // break;
- // default:
- // continue;
- // }
- // }
- // return "";
- // }
- // std::string cookie_var(const std::string& name)
- // {
- // return getenv(name);
- // }
- // std::string env_var(const std::string& name)
- // {
- // return getenv(name);
- // }
- //private:
- // /// Read a name/value pair from stdin
- // /**
- // * @return true if the name (of the n/v pair) read == name
- // */
- // bool read_var(std::string& name)
- // {
- // char ch;
- // std::string n;
- // std::string v;
- // while( std::cin.get(ch) && bytes_left_-- )
- // {
-
-
- // std::getline(std::cin, n, '=');
- // std::getline(std::cin, v, '&');
-
-
- // int bytes_left_;
- // var_map get_vars_;
- // var_map post_vars_;
- //};
-
} // namespace cgi
#include "detail/pop_options.hpp"
Added: sandbox/SOC/2007/cgi/libs/cgi/example/servers/fcgi_threadpool_server.cpp
==============================================================================
--- (empty file)
+++ sandbox/SOC/2007/cgi/libs/cgi/example/servers/fcgi_threadpool_server.cpp 2007-08-10 22:15:23 EDT (Fri, 10 Aug 2007)
@@ -0,0 +1,108 @@
+#include <boost/thread.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <boost/cgi/fcgi.hpp>
+
+
+namespace cgi {
+
+ class fcgi_threadpool_server
+ : public boost::enable_shared_from_this<fcgi_threadpool_server>
+ {
+ public:
+ typedef fcgi_threadpool_server type;
+ typedef cgi::fcgi_service service_type;
+ typedef cgi::fcgi_acceptor acceptor_type;
+ typedef boost::function<int (cgi::fcgi_request&)> handler_type;
+
+ /// Constructor
+ fcgi_threadpool_server(int num_threads, const handler_type& handler)
+ : service_()
+ , acceptor_(service_)
+ , num_threads_(num_threads)
+ , handler_(handler)
+ , thread_group()
+ {
+ start_accept();
+ }
+
+ /// Run the io_service(s) that service_ uses
+ void run()
+ {
+ for (int i = num_threads_; i != 0; i++)
+ {
+ thread_group_.create_thread
+ (boost::bind(&io_service::run, boost::ref(service_.io_service())));
+ }
+ thread_group_.join_all();
+ }
+
+ void stop()
+ {
+ acceptor_.cancel();
+ acceptor_.close();
+ }
+
+ void start_accept()
+ {
+ request_type::pointer new_request(new request_type(service_));
+ acceptor_.async_accept(new_request
+ , boost::bind(&type::accept_handler
+ , shared_from_this(), new_request
+ , boost::arg<1>));
+ }
+
+ /// If no errors, asynchronously load the request and call start_accept() again
+ void handle_accept(request_type::pointer req, boost::system::error_code& ec)
+ {
+ if (!ec)
+ {
+ req->async_load(boost::bind(&type::handle_load, req, boost::arg<1>));
+ start_accept();
+ }
+ }
+
+ /// If no errors, call handler_(req) in a background thread
+ void handle_load(request_type::pointer req, boost::system::error_code& ec)
+ {
+ if (!ec)
+ {
+ service_.post(boost::bind(&handler_type, handler_
+ , boost::ref(*new_request)));
+ }
+ }
+
+ private:
+ service_type service_;
+ acceptor_type acceptor_;
+
+ int num_threads_;
+ handler_type handler_;
+ boost::thread::thread_group thread_group_;
+ };
+
+} // namespace cgi
+
+
+/// The function that handles a request
+int sub_main(cgi::fcgi_request& req)
+{
+ // ...
+ // Use the request here
+ // ...
+
+ // Close the request; return "200 OK" to the client and '0' to the server
+ return req.close(cgi::http::ok, 0);
+}
+
+
+int main()
+{
+ // Create a server with 10 threads handling requests
+ fcgi_threadpool_server server(10, &sub_main);
+
+ // Run the server
+ server.run();
+
+ return 0;
+}
Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk