PageRenderTime 225ms CodeModel.GetById 100ms app.highlight 19ms RepoModel.GetById 102ms app.codeStats 0ms

/boost/cgi/common/response.hpp

http://github.com/darrengarvey/cgi
C++ Header | 339 lines | 148 code | 62 blank | 129 comment | 6 complexity | d2f35c23809665a76b7b72dfbf2423c2 MD5 | raw file
  1//                   -- response.hpp --
  2//
  3//            Copyright (c) Darren Garvey 2007.
  4// Distributed under the Boost Software License, Version 1.0.
  5//    (See accompanying file LICENSE_1_0.txt or copy at
  6//          http://www.boost.org/LICENSE_1_0.txt)
  7//
  8////////////////////////////////////////////////////////////////
  9#ifndef CGI_RESPONSE_HPP_INCLUDED__
 10#define CGI_RESPONSE_HPP_INCLUDED__
 11
 12#include "boost/cgi/detail/push_options.hpp"
 13
 14#include <string>
 15///////////////////////////////////////////////////////////
 16#include <boost/bind.hpp>
 17#include <boost/foreach.hpp>
 18#include <boost/shared_ptr.hpp>
 19///////////////////////////////////////////////////////////
 20#include "boost/cgi/common/cookie.hpp"
 21#include "boost/cgi/common/header.hpp"
 22#include "boost/cgi/http/status_code.hpp"
 23#include "boost/cgi/import/streambuf.hpp"
 24#include "boost/cgi/detail/throw_error.hpp"
 25#include "boost/cgi/fwd/basic_request_fwd.hpp"
 26#include "boost/cgi/config.hpp"
 27
 28BOOST_CGI_NAMESPACE_BEGIN
 29 namespace common {
 30
 31  /// The response class: a helper for responding to requests.
 32  /**
 33   * This class has no knowledge of requests or protocols. It is a lightweight
 34   * wrapper over a std::ostream that separates response headers from the
 35   * response body.
 36   *
 37   * One response can be reused for several requests (though not
 38   * simultaneously) and multiple responses can be combined.
 39   *
 40   * Note that std::endl is not supported for this class. std::endl usually
 41   * flushes an std::ostream, which is not possible with this class as it
 42   * is request-agnostic.
 43   *
 44   * When a response has been constructed, you can send it to any
 45   * SyncWriteStream by calling send() or flush().
 46   */
 47  template<typename CharT>
 48  class basic_response
 49  {
 50  public:
 51    typedef basic_response<CharT>              self_type;
 52    typedef CharT                              char_type;
 53    typedef typename std::basic_string<CharT>  string_type;
 54    typedef typename std::basic_ostream<CharT> ostream_type;
 55
 56    basic_response(common::http::status_code sc = common::http::ok);
 57
 58    /// Construct with a particular buffer
 59    /**
 60     * Takes the buffer and uses it internally, does nothing with it on
 61     * destruction.
 62     */
 63    basic_response(::BOOST_CGI_NAMESPACE::common::streambuf* buf,
 64        common::http::status_code sc = common::http::ok);
 65
 66    ~basic_response();
 67
 68    /// Clear the response buffer.
 69    void clear(bool clear_headers = true);
 70
 71    /// Return the response to the 'just constructed' state.
 72    void reset();
 73
 74    /// Write characters to the response body.
 75    std::size_t write(const char_type* str, std::size_t len);
 76
 77    /// Write a string to the response body.
 78    std::size_t write(string_type const& str);
 79
 80    template<typename ConstBufferSequence>
 81    std::size_t write(const ConstBufferSequence& buf);
 82
 83    /// Synchronously flush the data to the supplied SyncWriteStream.
 84    /**
 85     * This call uses throwing semantics. ie. an exception will be thrown on
 86     * any failure.
 87     * If there is no error, the buffer is cleared.
 88     */
 89    template<typename SyncWriteStream>
 90    void flush(SyncWriteStream& sws);
 91    
 92    /// Put a character into the stream.
 93    self_type& put (char_type c);
 94
 95    /// Synchronously flush the data via the supplied SyncWriteStream.
 96    /**
 97     * This call uses error_code semantics. ie. ec is set if an error occurs.
 98     * If there is no error, the buffer is cleared.
 99     */
100    template<typename SyncWriteStream>
101    boost::system::error_code
102      flush(SyncWriteStream& sws, boost::system::error_code& ec);
103
104    /// Synchronously send the data via the supplied request.
105    /**
106     * This call uses throwing semantics. ie. an exception will be thrown on
107     * any failure.
108     * Note: The data in the stream isn't cleared after this call.
109     */
110    template<typename SyncWriteStream>
111    void send(SyncWriteStream& sws);
112
113    /// Synchronously send the data via the supplied request.
114    /**
115     * This call will not throw, but will set `ec` such that `ec == true` if
116     * an error occurs. Details of the error are held in the `error_code`
117     * object.
118     */
119    template<typename SyncWriteStream>
120    boost::system::error_code
121      send(SyncWriteStream& sws, boost::system::error_code& ec);
122
123    /// Resend headers + content regardless of value of `headers_terminated_`.
124    template<typename SyncWriteStream>
125    void resend(SyncWriteStream& sws);
126
127    /// Asynchronously send the data through the supplied AsyncWriteStream.
128    /**
129     * Note: This is quite crude at the moment and not as asynchronous as
130     *       it could/should be. The data in the stream isn't cleared after
131     *       this call.
132     */
133    template<typename AsyncWriteStream, typename Handler>
134    void async_send(AsyncWriteStream& aws, Handler handler);
135
136    template<typename AsyncWriteStream, typename Handler>
137    void do_async_send(AsyncWriteStream& aws, Handler handler);
138
139    /// Get the buffer associated with the stream
140    common::streambuf*
141      rdbuf();
142
143    /// Set the status code associated with the response.
144    basic_response<char_type>&
145      status(const http::status_code& num);
146
147    /// Get the status code associated with the response.
148    http::status_code status() const;
149
150    /// Allow more headers to be added (WARNING: avoid using this).
151    void unterminate_headers();
152
153    /// Get the length of the body of the response (ie. not including the headers).
154    std::size_t content_length();
155
156    /// Add a header after appending the CRLF sequence.
157    basic_response<char_type>&
158      set(basic_header<char_type> const& hdr)
159    {
160      if (hdr.content.empty())
161        end_headers();
162      else
163        set_header(hdr.content);
164      return *this;
165    }
166
167    /// Add a cookie to the response.
168    basic_response<char_type>&
169      set(const basic_cookie<char_type>& ck)
170    {
171      set_header("Set-Cookie", ck.to_string());
172      return *this;
173    }
174
175    /// Set a header give a string of the form "name=value".
176    basic_response<char_type>&
177      set_header(const string_type& value);
178
179    /// Format and add a header given name and value, appending CRLF.
180    basic_response<char_type>&
181      set_header(string_type const& name, string_type const& value);
182
183    /// Get the contents of the response as a string.
184    /**
185     * This copies the contents of the response into a string.
186     * Headers aren't included unless `include_header` is true.
187     */
188    string_type str(bool include_header = false) const;
189
190    /// Get the value of a header in this response with the name `name`.
191    string_type header_value(string_type const& name);
192
193    /// Returns true for existing header names.
194    bool has_header_value(string_type const& name);
195
196
197    /// Clear all of the response headers.
198    void clear_headers();
199
200    void reset_headers();
201    
202    /// Get the charset.
203    string_type& charset() const { return charset_; }
204
205    /// Set the charset.
206    void charset(string_type const& cs) { charset_ = cs; }
207
208    /// Check if more response headers may be added.
209    bool headers_terminated() const;
210
211    // Is this really necessary?
212    void end_headers();
213
214    /// Get the ostream containing the response body.
215    ostream_type& ostream();
216
217    /// Get the response headers.
218    std::vector<string_type>& headers();
219
220    /// Stream any type to the response.
221    template<typename T>
222    self_type& operator<<(T t)
223    {
224      ostream_<< t;
225      return *this;
226    }
227
228    /// Change the charset of the response.
229    self_type& operator<< (charset_header<char_type> const& hdr)
230    {
231      charset(hdr.content);
232      return *this;
233    }
234 
235    /// Add a header to the response.
236    self_type& operator<< (basic_header<char_type> const& hdr)
237    {
238      return set(hdr);
239    }
240 
241    /// Add a cookie to the response.
242    self_type& operator<< (basic_cookie<char_type> const& ck)
243    {
244      return set(ck);
245    }
246 
247    /// Set the HTTP status of the response.
248    self_type& operator<< (http::status_code stat)
249    {
250      status(stat);
251      return *this;
252    }
253
254    /// Copy another response into this response.
255    /**
256     * The body of the other response is appended to the body of this
257     * response. The headers of the other response are added to this
258     * response, except for the "Content-type" header. The content-type
259     * of this response is assumed to be authoritative.
260     */
261    self_type& operator<< (self_type& other)
262    {
263      if (!headers_terminated())
264      {
265        typedef std::vector<std::string>::const_iterator iter_t;
266        for(iter_t iter (other.headers().begin()), end (other.headers().end());
267            iter != end;
268            ++iter
269          )
270        {
271          if (iter->substr(0,13) != "Content-type:") // Don't overwrite the content-type.
272            headers_.push_back(*iter);
273        }
274      }
275      ostream_<< other.ostream().rdbuf();
276      return *this;
277    }
278 
279  protected:
280    // Vector of all the headers, each followed by a CRLF
281    std::vector<string_type> headers_;
282
283    // The buffer is a shared_ptr, so you can keep it cached elsewhere.
284    boost::shared_ptr<common::streambuf> buffer_;
285
286    ostream_type ostream_;
287
288    http::status_code http_status_;
289
290    // True if no more headers can be appended.
291    bool headers_terminated_;
292
293    string_type charset_;
294
295 private:
296    // Send the response headers and mark that they've been sent.
297    template<typename ConstBufferSequence>
298    void prepare_headers(ConstBufferSequence& headers);
299   };
300
301   /// Typedefs for typical usage.
302   typedef basic_response<char>    response;
303
304 } // namespace common
305BOOST_CGI_NAMESPACE_END
306
307
308
309  /// Generic ostream template
310  /*
311  template<typename CharT, typename T>
312  BOOST_CGI_NAMESPACE::common::basic_response<CharT>&
313    operator<< (BOOST_CGI_NAMESPACE::common::basic_response<CharT>& resp, T t);
314  */
315  /// You can stream a BOOST_CGI_NAMESPACE::cookie into a response.
316  /**
317   * This is just a shorthand way of setting a header that will set a
318   * client-side cookie.
319   *
320   * You cannot stream a cookie to a response after the headers have been
321   * terminated. In this case, an alternative could be to use the HTML tag:
322   * <meta http-equiv="Set-cookie" ...> (see http://tinyurl.com/3bxftv or
323   * http://tinyurl.com/33znkj), but this is outside the scope of this
324   * library.
325   */
326              
327  /*
328   template<typename charT>
329  BOOST_CGI_NAMESPACE::common::basic_response<charT>&
330    operator<< (BOOST_CGI_NAMESPACE::common::basic_response<charT>&
331               , BOOST_CGI_NAMESPACE::common::basic_cookie<charT>);
332  */             
333#include "boost/cgi/detail/pop_options.hpp"
334
335#if !defined(BOOST_CGI_BUILD_LIB)
336#  include "boost/cgi/impl/response.ipp"
337#endif
338
339#endif // CGI_RESPONSE_HPP_INCLUDED__