|
Boost-Commit : |
From: drrngrvy_at_[hidden]
Date: 2007-05-25 08:44:50
Author: drrngrvy
Date: 2007-05-25 08:44:50 EDT (Fri, 25 May 2007)
New Revision: 4243
URL: http://svn.boost.org/trac/boost/changeset/4243
Log:
adding basic prototypes of basic_request, basic_service, gateway, and the generic request class
Added:
sandbox/SOC/2007/cgi/boost/cgi/basic_request.hpp
sandbox/SOC/2007/cgi/boost/cgi/basic_service.hpp
sandbox/SOC/2007/cgi/boost/cgi/gateway.hpp
sandbox/SOC/2007/cgi/boost/cgi/request.hpp
sandbox/SOC/2007/cgi/boost/cgi/request_base.hpp
Added: sandbox/SOC/2007/cgi/boost/cgi/basic_request.hpp
==============================================================================
--- (empty file)
+++ sandbox/SOC/2007/cgi/boost/cgi/basic_request.hpp 2007-05-25 08:44:50 EDT (Fri, 25 May 2007)
@@ -0,0 +1,67 @@
+#ifndef CGI_BASIC_REQUEST_HPP_INCLUDE_
+#define CGI_BASIC_REQUEST_HPP_INCLUDE_
+
+#include <boost/noncopyable.hpp>
+
+namespace cgi {
+
+ /// The 'role' of the request
+ /**
+ * See the section on 'role types' in the Design notes for more information.
+ * @code responder @endcode is by far the most common type
+ */
+ enum role_type
+ { undefined
+ , responder
+ , authorizer // American spelling, yes
+ , filter
+ };
+
+ enum status_type
+ { ok
+ , aborted
+ , ended
+ };
+
+ template< typename ServiceType, typename Allocator = std::allocator() >
+ class basic_request
+ : public request_base
+ , private boost::noncopyable
+ {
+ public:
+ typedef ServiceType service_type;
+ typedef typename ServiceType::protocol_type protocol_type;
+
+ explicit basic_request(service_type& s)
+ : service_(s)
+ , role_(undefined)
+ {
+ }
+
+ /// Notify the server the request has finished being handled
+ /**
+ * In certain situations (such as a Proactor client using the async read
+ * functions) it will be necessary to call end, rather than just returning
+ * from the sub_main function.
+ *
+ * @param status_code This value is returned to the server indicating the
+ * 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).
+ */
+ void end(int status_code)
+ {
+ service_.end_request(this, status_code);
+ status_ = status_type::ended;
+ }
+ private:
+ service_type& service_;
+ role_type role_;
+ int status_code_; // the what to return to the server on request completion
+ status_type status_;
+ };
+
+} // namespace cgi
+
+#endif // CGI_BASIC_REQUEST_HPP_INCLUDE_
+
Added: sandbox/SOC/2007/cgi/boost/cgi/basic_service.hpp
==============================================================================
--- (empty file)
+++ sandbox/SOC/2007/cgi/boost/cgi/basic_service.hpp 2007-05-25 08:44:50 EDT (Fri, 25 May 2007)
@@ -0,0 +1,100 @@
+#ifndef CGI_BASIC_SERVICE_HPP_INCLUDE__
+#define CGI_BASIC_SERVICE_HPP_INCLUDE__
+
+#include <boost/noncopyable.hpp>
+#include <boost/asio.hpp>
+
+namespace cgi {
+
+ /*
+ * This should inherit another object, such as service_base, which takes the
+ * protocol as a template arguement. Things like the mutexes don't need to be
+ * here for standard CGI.
+ *
+ * The question is whether the standard CGI service is so different from the
+ * more general network-interface case that the whole class should
+ * specialised for it (it probably should be).
+ */
+ template< typename Protocol >
+ class basic_service
+ : public boost::asio::io_service::service
+ , private boost::noncopyable
+ {
+ public:
+ typedef Protocol protocol;
+ typedef basic_request<Protocol> request_type;
+ typedef basic_service<Protocol> type;
+ typedef service_options<Protocol> service_options; // just an idea
+
+ template< typename Handler >
+ explicit basic_service(Handler handler, service_options& opts)
+ : work_io_service()
+ , boost::asio::io_service::service(work_io_service_)
+ , work_(new boost::asio::io_service::work(work_io_service_))
+ , gateway_(this, opts.max_concurrent_connections)
+ {
+ }
+
+ /**
+ * If the user passes in an asio::io_service, set that as the 'owner'
+ * instead of the (internal) work_io_service_ instance.
+ */
+ template< typename Handler >
+ explicit basic_service( boost::asio::io_service& ios, Handler handler
+ , service_options& opts )
+ : work_io_service()
+ , boost::asio::io_service::service(ios)
+ , work_(new boost::asio::io_service::work(work_io_service_))
+ , gateway_(this, opts.max_concurrent_connections)
+ {
+ }
+
+ /// Block until a request can be returned
+ /**
+ * This function is called in the construction of a @code basic_request
+ * @endcode, or a general @code request @endcode. It either returns a
+ * valid request from the request queue immediately, or blocks on a
+ * condition variable.
+ *
+ * Before blocking, if there are available connection slots then another
+ * is @code accept() @endcode ed.
+ */
+ request_type& get_request()
+ {
+ boost::mutex::scoped_lock lk(mutex_);
+ if( !request_queue_.empty() )
+ {
+ request_type& req = request_queue_.front();
+ request_queue_.pop();
+ return req;
+ }
+ // see gateway::accept() reference about this
+ if( gateway_.accept() )
+ {
+ // parse the request now and return it
+ }
+ cond_.wait(lk);
+ if( !request_queue_.empty() )
+ {
+ request_type& req = request_queue_.front();
+ request_queue_.pop();
+ return req;
+ }
+ }
+
+ private:
+ static boost::asio::io_service::id id;
+ boost::asio::io_service work_io_service_;
+ boost::scoped_ptr<boost::io_service::work> work_;
+
+ gateway gateway_;
+
+
+ boost::mutex mutex_;
+ boost::condition cond_;
+ };
+
+} // namespace cgi
+
+#endif // CGI_BASIC_SERVICE_HPP_INCLUDE__
+
Added: sandbox/SOC/2007/cgi/boost/cgi/gateway.hpp
==============================================================================
--- (empty file)
+++ sandbox/SOC/2007/cgi/boost/cgi/gateway.hpp 2007-05-25 08:44:50 EDT (Fri, 25 May 2007)
@@ -0,0 +1,144 @@
+#ifndef CGI_GATEWAY_HPP_INCLUDE__
+#define CGI_GATEWAY_HPP_INCLUDE__
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace cgi {
+
+/// The gateway class manages connections
+/**
+ * New connections are accepted through here, via a call to accept();
+ * used/corrupted connections are closed via a call to stop(conn_ptr);
+ * all connections are closed via a call to stop_all().
+ *
+ * If you want to use the gateway after a call to stop_all(), you must
+ * call reset() and pass it the maximum number of connections the gateway
+ * can take.
+ */
+template< typename CommonGatewayService >
+class gateway
+{
+public:
+ typedef CommonGatewayService service_type;
+ typedef typename CommonGatewayService::protocol_type protocol_type;
+ typedef connection<protocol_type> connection_type;
+ typedef boost::shared_ptr<connection_type> conn_ptr;
+
+ /// Constructor
+ explicit gateway(service_type& service, int max_conns)
+ : service_(service)
+ , acceptor_(service.io_service())
+ , available_slots_(max_conns)
+ {
+ //accept();
+ }
+
+ /// Destructor
+ ~gateway()
+ {
+ stop_all();
+ }
+
+ /// Start an asychronous accept
+ /**
+ * This returns false unless the connection is already established. This is
+ * true with standard CGI, for example, where the connection is simply a
+ * wrapper over standard input/output.
+ *
+ * If there is a need to differentiate between a fail and a 'waiting' accept
+ * then a tribool could be returned from here.
+ */
+ bool accept()
+ {
+ if( available_slots_ > 0 )
+ {
+ conn_ptr nc(connection_type::create());
+ acceptor_.async_accept( nc->socket()
+ , boost::bind( &cgi::gateway::handle_accept
+ , this, nc, boost::placeholders::error
+ )
+ );
+ }
+ return false;
+ }
+
+ void handle_accept(conn_ptr conn, boost::system::error_code& error)
+ {
+ if( !error )
+ {
+ start(conn);
+ //accept();
+ }
+ }
+
+ /// Cleanly start the connection
+ void start(conn_ptr cptr)
+ {
+ connections_.insert(cptr);
+ --available_slots_;
+ cptr->start();
+ }
+
+ /// Cleanly stop the connection
+ void stop(conn_ptr cptr)
+ {
+ connections_.erase(cptr);
+ ++available_slots_;
+ cptr->stop();
+ }
+
+ /// Cleanly stop all connections
+ void stop_all()
+ {
+ available_slots_ = 0; // make sure subsequent accept()s fail
+ std::for_each( connection_.begin(), connections_.end()
+ , boost::bind(&gateway::stop, _1)
+ );
+ available_slots_ = 0;
+ connections_.clear();
+ }
+
+ /// Reset the gateway
+ /**
+ * All connections are gracefully closed and then the gateway is set up for
+ * reuse.
+ *
+ * @param max_connections the available slots is reset to this value
+ */
+ void reset(int max_connections)
+ {
+ stop_all();
+ available_slots_ = max_connections;
+ }
+
+ /// Increment the number of allowed connection slots
+ void incr_slots(int slots = 1) { available_slots_ += slots; }
+ /// Decrement the number of allowed connection slots
+ void decr_slots(int slots = 1) { available_slots_ -= slots; }
+
+private:
+ service_type& service_;
+ acceptor<service_type> acceptor_;
+ int available_slots_;
+ std::set<conn_ptr> connections_;
+};
+
+} // namespace cgi
+
+#endif // CGI_GATEWAY_HPP_INCLUDE__
+
+/*
+ * Notes:
+ *
+ * 1. A FastCGI server doesn't have to use tcp sockets: it can use pipes
+ * instead. Support for these will only come in when Boost.Asio supports them.
+ *
+ * 2. For now each request object could hold a pointer/reference to the
+ * connection that it's associated with. For the forseable future that's going
+ * to be enough. However, since the FastCGI spec doesn't say otherwise,
+ * eventually it could happen that the response can be sent back via ANY open
+ * connection. In that case, the request would have to query the gateway to
+ * find an available connection before sending through it. For now, that's
+ * unneccesary.
+ */
Added: sandbox/SOC/2007/cgi/boost/cgi/request.hpp
==============================================================================
--- (empty file)
+++ sandbox/SOC/2007/cgi/boost/cgi/request.hpp 2007-05-25 08:44:50 EDT (Fri, 25 May 2007)
@@ -0,0 +1,99 @@
+//
+// request.hpp
+// -----------
+//
+// Copyright (c) 2007 Darren Garvey
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef __CGI_REQUEST_HPP_INCLUDE_
+#define __CGI_REQUEST_HPP_INCLUDE_
+
+#include <boost/noncopyable.hpp>
+#include "request_base.hpp"
+#include "basic_cgi_service_fwd.hpp"
+#include "basic_cgi_request_fwd.hpp"
+
+namespace cgi {
+
+/// The 'request' class that is the user's main entry point into this library
+/**
+ * By default the class will work with 'any' service type to provide uniform
+ * access to requests handled by any cgi::service type. If no service is
+ * provided to the constructor, the object is assumed to be a standard cgi
+ * object.
+ *
+ * @par Example
+ * @code
+ * void f()
+ * {
+ * cgi::request r; // standard cgi object
+ * }
+ *
+ * void g()
+ * {
+ * cgi::fcgi_service service; // fastcgi service
+ * cgi::request(service); // fastcgi request
+ * }
+ * @endcode
+ *
+ * If runtime linkage is insufficient, a macro can be defined to instead use
+ * compile-time linking. Available macros:
+ *
+ * @li @code BOOST_CGI_IMPLICIT_CGI @endcode -> standard cgi
+ * @li @code BOOST_CGI_IMPLICIT_FCGI @endcode -> fastcgi
+ *
+ * {{can modern compilers optimize away the runtime element of the general
+ * version of request?}}
+ */
+
+#if defined(BOOST_CGI_IMPLICIT_CGI)
+ typedef basic_cgi_request<protocol::cgi> request;
+#elif defined(BOOST_CGI_IMPLICIT_FCGI)
+ typedef basic_cgi_request<protocol::fcgi> request;
+#elif defined(BOOST_CGI_IMPLICIT_SCGI)
+# error "SCGI driver not implemented yet"
+#else
+
+ // Below is the 'general' request class
+ class request
+ : private boost::noncopyable
+ {
+ boost::shared_ptr<request_base> impl_;
+ public:
+ template< typename Protocol, typename Allocator = std::allocator() >
+ explicit request(basic_cgi_service<Protocol, Allocator>& service)
+ : impl_(&service.get_request())
+ {
+ }
+
+ template< typename ServiceType, typename Allocator = std::allocator() >
+ request(basic_cgi_request<ServiceType, Allocator>& request)
+ : impl_(&request)
+ {
+ }
+
+ template< typename Allocator = std::allocator() >
+ request()
+ : impl_(new basic_cgi_request< basic_cgi_service<protocol::cgi, Allocator>
+ , Allocator
+ > request
+ )
+ {
+ }
+
+ void end( int return_code )
+ {
+ impl_->end(return_code);
+ }
+
+ // more functions
+ };
+
+} // namespace cgi
+
+#endif // __CGI_REQUEST_HPP_INCLUDE_
+
Added: sandbox/SOC/2007/cgi/boost/cgi/request_base.hpp
==============================================================================
--- (empty file)
+++ sandbox/SOC/2007/cgi/boost/cgi/request_base.hpp 2007-05-25 08:44:50 EDT (Fri, 25 May 2007)
@@ -0,0 +1,90 @@
+#ifndef CGI_REQUEST_BASE_HPP_INCLUDE_
+#define CGI_REQUEST_BASE_HPP_INCLUDE_
+
+namespace cgi {
+
+ /// ABC that defines the basic interface for cgi::basic_cgi_request<>s
+ /**
+ * This class also allows for the general (rather than generic) verision
+ * of cgi::request, which can take any type of request.
+ */
+ class request_base
+ {
+ public:
+ virtual ~request() { }
+
+ /// Notify the server that the request has been handled
+ /**
+ * Under normal circumstances {{ideally}}, this won't be called internally
+ * but a user may safely call it anyway, if (for example) they have a lot
+ * of post-request-handling housekeeping to do but want the server to
+ * finish with that request.
+ *
+ * @param return_code This is equivalent to the return value from main() in
+ * a standard cgi library, or sub_main() in the provided examples. 0 should
+ * denote success, anything else an error, however the number will
+ * generally be ignored by the server.
+ */
+ virtual void end( int return_code ) = 0;
+
+ /// Returns the number of bytes available in the read buffer
+ virtual std::size_t available() = 0;
+
+ /// Get the streambuf associated with the request
+ /**
+ * You may want to construct your own i/ostream instead of using the
+ * request directly.
+ *
+ * @note You should NOT use std::cout/std::cin for writes, as they are
+ * not thread aware and in some cases won't exist (eg. in a strict fastcgi
+ * impletentation).
+ *
+ * {{should this return an asio::streambuf instead?}}
+ */
+ virtual std::streambuf* rdbuf() = 0;
+
+ /// Flush the output buffer now
+ /**
+ * Since the output for a request is buffered until the request has
+ * completed (see Design notes), under low-memory or large-output
+ * conditions, incrementally flushing the buffer may be necessary
+ *
+ * @note If something goes awry after calling this, the user will
+ * be left with half a reply, rather than a (cleaner) error page.
+ */
+ virtual void flush() = 0;
+
+ /// Write a message to the error output
+ /**
+ * In a standard cgi program, this is equivalent to writing to std::cerr,
+ * which should be closed in fastcgi (although many implementations leave
+ * it writable). In order to write an error message in a cross-platform
+ * way, you should use this function.
+ *
+ * @note On Apache and lighttpd the messages are appended to the server
+ * log file, although there is no guarantee that other platforms will
+ * recognise it.
+ */
+ virtual void log( const std::string& error_message ) = 0;
+
+ /// Get the role of the current request
+ /**
+ * The FastCGI 1.0 protocol specifies three types of request:
+ *
+ * @li Responder: the familiar type
+ *
+ * @li Authorizer: the server provides client information and the
+ * application responds with a true/false response, plus optional data
+ * about where to redirect the client and with what extra variables
+ *
+ * @li Filter: the program is provided with data and a file, which it is
+ * expected to 'filter' and then return to the server. This is poorly
+ * supported in production servers {{AFAIK}}
+ */
+ virtual role_type role() = 0;
+ };
+
+} // namespace cgi
+
+#endif // CGI_REQUEST_BASE_HPP_INCLUDE_
+
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