/boost/cgi/fcgi/client.hpp
C++ Header | 262 lines | 172 code | 28 blank | 62 comment | 17 complexity | b4445930d3799be5858f48287b4fe801 MD5 | raw file
- // -- fcgi/client.hpp --
- //
- // Copyright (c) Darren Garvey 2007.
- // 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_FCGI_CLIENT_HPP_INCLUDED__
- #define CGI_FCGI_CLIENT_HPP_INCLUDED__
- #include <vector>
- ///////////////////////////////////////////////////////////
- #include <boost/shared_ptr.hpp>
- #include <boost/logic/tribool.hpp>
- #include <boost/asio/buffer.hpp>
- ///////////////////////////////////////////////////////////
- #include "boost/cgi/basic_client.hpp"
- #include "boost/cgi/common/map.hpp"
- #include "boost/cgi/fcgi/traits.hpp"
- #include "boost/cgi/connections/shareable_tcp_socket.hpp"
- #include "boost/cgi/detail/throw_error.hpp"
- #include "boost/cgi/fcgi/specification.hpp"
- #include "boost/cgi/fwd/basic_request_fwd.hpp"
- #include "boost/cgi/error.hpp"
- #include "boost/cgi/import/read.hpp"
- #include "boost/cgi/import/buffer.hpp"
- #include "boost/cgi/import/io_service.hpp"
- #undef min
- #undef max
- #include <algorithm>
- #if !defined(BOOST_CGI_NO_LOGGING) && !defined(NDEBUG)
- # include <iostream>
- #endif
- BOOST_CGI_NAMESPACE_BEGIN
- namespace common {
- /// A client that uses a TCP socket that owned by it.
- /// Construct
- template<>
- basic_client<
- ::BOOST_CGI_NAMESPACE::common::tags::fcgi
- >::basic_client()
- : request_id_(-1)
- , status_(none_)
- , total_sent_bytes_(0)
- , total_sent_packets_(0)
- , header_()
- , outbuf_()
- , keep_connection_(false)
- {
- }
- /// Override basic_client::close().
- /**
- * Closing a FastCGI means sending an END_REQUEST header
- * to the HTTP server and potentially closing the connection.
- *
- * Note that in general the HTTP server is responsible for the
- * lifetime of the connection, but can hand that control over
- * to the library (eg. if the server is set up to recycle
- * connections after N requests).
- */
- template<>
- boost::system::error_code
- basic_client<
- ::BOOST_CGI_NAMESPACE::common::tags::fcgi
- >::close(boost::uint64_t app_status, boost::system::error_code& ec)
- {
- // Note that the request may already be closed if the client aborts
- // the connection.
- if (!is_open())
- ec = error::already_closed;
- else
- {
- if ((constructed < status_) && (status_ < end_request_sent))
- {
- status_ = closed_;
- // Write an EndRequest packet to the server.
- outbuf_.clear();
- header_.reset(fcgi::spec_detail::END_REQUEST, request_id_, 8);
- fcgi::spec::end_request_body body(app_status, fcgi::spec_detail::REQUEST_COMPLETE);
- outbuf_.push_back(header_.data());
- outbuf_.push_back(body.data());
- write(*connection_, outbuf_, boost::asio::transfer_all(), ec);
- }
- status_ = closed_;
- if (!ec && !keep_connection_)
- connection_->close();
- }
- return ec;
- }
- /// Prepare a buffer by wrapping it into a FastCGI packet.
- /**
- * FastCGI dictates that data is sent in packets which identify
- * which request the data relates to, along with the size of
- * the packet.
- *
- * This function takes a buffer of data and creates another
- * buffer which includes the packet header information. As the
- * buffers themselves only contain pointers to the data the
- * overhead is negligible as the data is not copied.
- *
- * The lifetime of the header is guaranteed as it is kept in
- * the client object itself.
- */
- template<>
- template<typename ConstBufferSequence>
- void
- basic_client<
- ::BOOST_CGI_NAMESPACE::common::tags::fcgi
- >::prepare_buffer(const ConstBufferSequence& buf)
- {
- typename ConstBufferSequence::const_iterator iter = buf.begin();
- typename ConstBufferSequence::const_iterator end = buf.end();
- outbuf_.clear();
- outbuf_.push_back(boost::asio::buffer(header_.data()));
- std::size_t total_buffer_size(0);
- for(; iter != end; ++iter)
- {
- boost::asio::const_buffer buffer(*iter);
- std::size_t new_buf_size( boost::asio::buffer_size(*iter) );
- if (total_buffer_size + new_buf_size
- > static_cast<std::size_t>(fcgi::spec::max_packet_size::value))
- {
- // If the send buffer is empty, extract a chunk of the
- // new buffer to send. If there is already some data
- // ready to send, don't add any more data to the pack.
- if (total_buffer_size == 0)
- {
- total_buffer_size
- = std::min<std::size_t>(new_buf_size,65500);
- /*
- std::cerr<< "Oversized buffer: " << total_buffer_size
- << " / " << new_buf_size << " bytes sent\n";
- */
- outbuf_.push_back(
- boost::asio::buffer(*iter, total_buffer_size));
- break;
- }
- else
- break;
- }
- else
- {
- total_buffer_size += new_buf_size;
- outbuf_.push_back(*iter);
- }
- }
- header_.reset(fcgi::spec_detail::STDOUT, request_id_, total_buffer_size);
- }
-
- template<>
- void
- basic_client<
- ::BOOST_CGI_NAMESPACE::common::tags::fcgi
- >::handle_write(std::size_t bytes_transferred, boost::system::error_code& ec)
- {
- total_sent_bytes_ += bytes_transferred;
- total_sent_packets_ += 1;
-
- std::size_t total_buffer_size = static_cast<std::size_t>(header_.content_length());
-
- #if !defined(BOOST_CGI_NO_LOGGING) && !defined(NDEBUG)
- std::ofstream log("../logs/fcgi_client.log", std::ios::out | std::ios::app);
- if (ec)
- std::cerr<< "Error " << ec << ": " << ec.message() << '\n';
- else
- //std::cerr
- log
- << "Transferred " << total_buffer_size
- << " (+" << (bytes_transferred - total_buffer_size)
- << " protocol) bytes (running total: "
- << total_sent_bytes_ << " bytes; "
- << total_sent_packets_ << " packets).\n";
- #endif // !defined(BOOST_CGI_NO_LOGGING) && !defined(NDEBUG)
- // Now remove the protocol overhead for the caller, who
- // doesn't want to count it.
- bytes_transferred -= fcgi::spec::header_length::value;
- // Check everything was written ok.
- if (!ec && bytes_transferred != total_buffer_size)
- ec = ::BOOST_CGI_NAMESPACE::fcgi::error::couldnt_write_complete_packet;
- }
- /// Write some data to the client.
- /**
- * Currently this actually writes an entire packet - which may be up
- * to 65,535 bytes in size - as the connection may potentially be
- * multiplexed and shared between requests.
- */
- template<>
- template<typename ConstBufferSequence>
- std::size_t
- basic_client<
- ::BOOST_CGI_NAMESPACE::common::tags::fcgi
- >::write_some(
- const ConstBufferSequence& buf
- , boost::system::error_code& ec
- )
- {
- prepare_buffer(buf);
-
- std::size_t bytes_transferred
- = boost::asio::write(*connection_, outbuf_
- , boost::asio::transfer_all(), ec);
- handle_write(bytes_transferred, ec);
-
- return bytes_transferred;
- }
- /// Write some data to the client.
- /**
- * Currently this actually writes an entire packet - which may be up
- * to 65,535 bytes in size - as the connection may potentially be
- * multiplexed and shared between requests.
- */
- template<>
- template<typename ConstBufferSequence, typename Handler>
- void
- basic_client<
- ::BOOST_CGI_NAMESPACE::common::tags::fcgi
- >::async_write_some(
- const ConstBufferSequence& buf
- , Handler handler
- )
- {
- prepare_buffer(buf);
- boost::system::error_code ec;
- std::size_t bytes_transferred
- = boost::asio::write(*connection_, outbuf_
- , boost::asio::transfer_all(), ec);
- handle_write(bytes_transferred, ec);
- handler(bytes_transferred, ec);
- }
- } // namespace common
- namespace fcgi {
- typedef
- common::basic_client<
- ::BOOST_CGI_NAMESPACE::common::tags::fcgi
- >
- client;
- } // namespace fcgi
- BOOST_CGI_NAMESPACE_END
- #endif // CGI_FCGI_CLIENT_HPP_INCLUDED__