PageRenderTime 370ms CodeModel.GetById 116ms app.highlight 127ms RepoModel.GetById 94ms app.codeStats 0ms

/boost/cgi/basic_request.hpp

http://github.com/darrengarvey/cgi
C++ Header | 668 lines | 367 code | 77 blank | 224 comment | 25 complexity | 5ed9e545f482abe2ce2ba4f5b2f2b1e1 MD5 | raw file
  1//                -- basic_request.hpp --
  2//
  3//          Copyright (c) 2007-2009 Darren Garvey.
  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//
 10// Defines the basic_request<> class; the main entry-point to the
 11// library.
 12//
 13////////////////////////////////////////////////////////////////
 14#ifndef CGI_BASIC_REQUEST_HPP_INCLUDED__
 15#define CGI_BASIC_REQUEST_HPP_INCLUDED__
 16
 17#include "boost/cgi/detail/push_options.hpp"
 18
 19#include <boost/shared_ptr.hpp>
 20#include <boost/noncopyable.hpp>
 21#include <boost/system/error_code.hpp>
 22///////////////////////////////////////////////////////////
 23#include "boost/cgi/common/request_data.hpp"
 24#include "boost/cgi/common/form_part.hpp"
 25#include "boost/cgi/common/map.hpp"
 26#include "boost/cgi/common/parse_options.hpp"
 27#include "boost/cgi/common/path_info.hpp"
 28#include "boost/cgi/common/protocol_traits.hpp"
 29#include "boost/cgi/common/request_service.hpp"
 30#include "boost/cgi/common/request_status.hpp"
 31#include "boost/cgi/common/role_type.hpp"
 32#include "boost/cgi/common/source_enums.hpp"
 33#include "boost/cgi/detail/basic_io_object.hpp"
 34#include "boost/cgi/detail/throw_error.hpp"
 35#include "boost/cgi/fwd/basic_request_fwd.hpp"
 36#include "boost/cgi/fwd/basic_protocol_service_fwd.hpp"
 37#include "boost/cgi/http/status_code.hpp"
 38#ifdef BOOST_CGI_ENABLE_SESSIONS
 39#  include "boost/cgi/utility/sessions.hpp"
 40#endif // BOOST_CGI_ENABLE_SESSIONS
 41
 42BOOST_CGI_NAMESPACE_BEGIN
 43 namespace common {
 44
 45  /// The basic_request class, primary entry point to the library
 46  /**
 47   * Note: By default, synchronous protocols (ie. cgi) auto-load AND parse
 48   * STDIN,whereas async protocols don't.
 49   *
 50   * Note: The alternative functions which take a boost::system::error_code
 51   * are the non-throwing versions. Instead of a boost::system::system_error
 52   * being thrown in case of an error, the passed error_code will be set to
 53   * the value of the error, s.t. if (error) evaluates to true.`
 54   * 
 55   * Note: This class isn't thread safe: carrying around a mutex-per-request
 56   * seems prohibitively expensive. There could be functions which take a
 57   * mutex as an argument and lock it. (Async calls could get messy if you
 58   * need a protected request object).
 59  **/
 60  template<typename Protocol>
 61  class basic_request
 62    : public detail::basic_io_object<
 63        typename protocol_traits<Protocol>::request_service_type
 64    >
 65  {
 66  public:
 67    typedef basic_request<Protocol>                    self_type;
 68    typedef Protocol                                   protocol_type;
 69    typedef protocol_traits<protocol_type>             traits;
 70    typedef typename traits::protocol_service_type     protocol_service_type;
 71    typedef typename traits::request_service_type      service_type;
 72    typedef typename service_type::implementation_type implementation_type;
 73    typedef typename traits::char_type                 char_type;
 74    typedef typename traits::string_type               string_type;
 75    typedef typename traits::request_type              request_type;
 76    typedef typename traits::client_type               client_type;
 77    typedef typename traits::buffer_type               buffer_type;
 78#ifdef BOOST_CGI_ENABLE_SESSIONS
 79    typedef typename traits::session_type              session_type;
 80
 81    session_type session;
 82#endif // BOOST_CGI_ENABLE_SESSIONS
 83    typedef boost::shared_ptr<request_type>            pointer;
 84    
 85    /// The environment data, exposed as a `request_data<env_map>`.
 86    env_data    env;
 87    /// The POST data, exposed as a `request_data<post_map>`.
 88    post_data   post;
 89    /// The GET (ie. query string) data, exposed as a `request_data<get_map>`.
 90    get_data    get;
 91    /// The form data, which is either the GET or POST data.
 92    form_data   form;
 93    /// The cookie data, exposed as a `request_data<cookie_map>`.
 94    cookie_data cookies;
 95    /// The file uploads, exposed as a `request_data<upload_map>`.
 96    upload_data uploads;
 97
 98    basic_request(
 99        int opts
100      , char** base_env = NULL)
101        : detail::basic_io_object<service_type>()
102    {
103      if ((parse_options)opts > parse_none) load((parse_options)opts, base_env);
104    }
105
106     basic_request(
107        const parse_options opts = traits::parse_opts
108      , char** base_env = NULL)
109        : detail::basic_io_object<service_type>()
110    {
111      set_protocol_service();
112      if (opts > parse_none) load(opts, base_env);
113    }
114
115    // Won't throw
116    basic_request(boost::system::error_code& ec
117                 , const parse_options opts = traits::parse_opts
118                 , char** base_env = NULL)
119      : detail::basic_io_object<service_type>()
120    {
121      set_protocol_service();
122      if (opts > parse_none) load(opts, ec);
123    }
124
125    // Throws
126    basic_request(protocol_service_type& s
127                 , const parse_options opts = traits::parse_opts
128                 , char** base_env = NULL)
129      : detail::basic_io_object<service_type>(s.get_io_service())
130    {
131      set_protocol_service(s);
132      if (opts > parse_none) load(opts, base_env);
133    }
134
135    // Won't throw
136    basic_request(protocol_service_type& s
137                 , boost::system::error_code& ec
138                 , const parse_options opts = traits::parse_opts
139                 , char** base_env = NULL)
140      : detail::basic_io_object<service_type>(s.get_io_service())
141    {
142      set_protocol_service(s);
143      if (opts > parse_none) load(opts, ec, base_env);
144    }
145
146    /// Make a new mutiplexed request from an existing connection.
147    // Throws.
148    basic_request(implementation_type& impl)
149      : detail::basic_io_object<service_type>(impl.service_->get_io_service())
150    {
151      set_protocol_service(*impl.service_);
152      boost::system::error_code ec;
153      this->service.begin_request_helper(this->implementation
154                                        , impl.header_buf_, ec);
155      detail::throw_error(ec);
156    }
157
158    /// Make a new mutiplexed request from an existing connection.
159    // Won't throw.
160    basic_request(implementation_type& impl, boost::system::error_code& ec)
161      : detail::basic_io_object<service_type>(impl.service_->get_io_service())
162    {
163      set_protocol_service(*impl.service_);
164      this->service.begin_request_helper(this->implementation
165                                        , impl.header_buf_, ec);
166    }
167      
168    ~basic_request()
169    {
170#ifdef BOOST_CGI_ENABLE_SESSIONS
171      try {
172          if (!session.id().empty())
173            this->service.session_manager().save(session);
174      } catch(...) {
175         // pass
176      }
177#endif // BOOST_CGI_ENABLE_SESSIONS
178    }
179    
180#ifdef BOOST_CGI_ENABLE_SESSIONS
181    /// Start a session, or load the session from a cookie.
182    /**
183     * This starts a session.
184	 */
185    void start_session()
186    {
187      if (!session.loaded())
188      {
189        // Assume cookies have been loaded. This will throw at runtime (in
190        // a debug build) if `request.load(parse_cookie)` hasn't been called
191        // by now.
192        this->service.session_manager().start(
193            session, cookies.pick(BOOST_CGI_SESSION_COOKIE_NAME, ""));
194      }
195    }
196
197    /// Stop the session and delete the stored session data.
198    void stop_session()
199    {
200      this->service.session_manager().stop(session);
201    }
202#endif // BOOST_CGI_ENABLE_SESSIONS
203
204    protocol_service_type& get_protocol_service()
205    {
206      return *(this->implementation.service_);
207    }
208    
209    static pointer create(protocol_service_type& ps)
210    {
211      return pointer(new self_type(ps));
212    }
213
214    static pointer create()
215    {
216      return pointer(new self_type());
217    }
218
219    void set_protocol_service()
220    {
221      //this->service.set_service(this->implementation, ps);
222    }
223
224    void set_protocol_service(protocol_service_type& ps)
225    {
226      this->service.set_service(this->implementation, ps);
227    }
228
229    /// The id of this request.
230    /**
231     * This is 1 for CGI/aCGI requests, but may be != 1 for FastCGI
232     * requests.
233     *
234     * Note that for FastCGI requests, the id's are assigned on a
235     * *per-connection* policy, so in one application you may have 
236     * several requests with the same id.
237     */
238    int id()
239    {
240      return this->service.request_id(this->implementation);
241    }
242
243    /// Check if the request is still open (ie. not aborted or closed)
244    bool is_open()
245    {
246      return this->service.is_open(this->implementation);
247    }
248
249    /// Synchronously read/parse the request meta-data
250    /**
251     * Note: 'loading' including reading/parsing STDIN if
252     * parse_stdin == true
253     */
254    void load(parse_options parse_opts = parse_env, char** base_env = NULL)
255    {
256      boost::system::error_code ec;
257      load(parse_opts, ec, base_env);
258      if (ec != error::eof)
259        detail::throw_error(ec);
260    }
261
262    // Error-code semantics
263    boost::system::error_code
264      load(parse_options parse_opts, boost::system::error_code& ec
265          , char** base_environment = NULL, bool is_command_line = true)
266    {
267      // Parse just the environment first, then check the user
268      // isn't trying to upload more data than we want to let them.
269      // Define `BOOST_CGI_POST_MAX` to set the maximum content-length
270      // allowed.
271      if (parse_opts & parse_env)
272      {
273        //this->service.load(this->implementation, parse_env, ec);
274        //if (content_length() >= BOOST_CGI_POST_MAX)
275        //  ec = common::error::max_post_exceeded;
276        this->service.load(this->implementation, parse_opts, ec);
277        
278        if (ec) return ec;
279
280        // Load the environment passed by the user.
281        if (base_environment)
282          this->service.load_environment(
283              this->implementation, base_environment
284            , is_command_line);
285        
286        if (parse_opts & parse_env)
287          env.set(env_vars(this->implementation.vars_));
288        if (parse_opts & parse_get_only)
289          get.set(get_vars(this->implementation.vars_));
290        if (parse_opts & parse_post_only) {
291          post.set(post_vars(this->implementation.vars_));
292          uploads.set(upload_vars(this->implementation.vars_));
293        }
294        if ((parse_opts & parse_cookies) == parse_cookies) {
295          cookies.set(cookie_vars(this->implementation.vars_));
296        }
297        if ((parse_opts & parse_form_only) == parse_form_only)
298        {
299          common::name rm(request_method().c_str());
300          form.set(rm == "POST" ? post.impl() : get.impl());
301        }
302#ifdef BOOST_CGI_ENABLE_SESSIONS
303        if (parse_opts & parse_session_only)
304        {
305          if (!!cookies && cookies.count(BOOST_CGI_SESSION_COOKIE_NAME))
306            session.id(cookies[BOOST_CGI_SESSION_COOKIE_NAME]);
307          else
308          if (traits::auto_start_session)
309            session.id(this->service.make_session_id());
310          if (!session.id().empty())
311            this->service.session_manager().load(session);
312        }
313#endif // BOOST_CGI_ENABLE_SESSIONS
314      }
315      return ec;
316    }
317
318    void load(char** base_environment, bool is_command_line = true)
319    {
320      this->service.load_environment(this->implementation
321                                    , base_environment
322                                    , is_command_line);
323    }
324
325    /// Get the buffer containing the POST data.
326    /**
327     * **FIXME**
328     * This actually returns the whole buffer on FastCGI at the moment, 
329     * which contains the params too.
330     */
331    buffer_type& post_buffer()
332    {
333      return this->implementation.post_buffer_;
334    }
335    
336    // **FIXME**
337    /// Asynchronously read/parse the request meta-data
338    /**
339     * Note: 'loading' including reading/parsing STDIN if
340     * parse_stdin == true
341     */
342    //template<typename Handler>
343    //void async_load(Handler handler, bool parse_stdin = false)
344    //{
345    //  this->service.async_load(this->implementation, parse_stdin
346    //                          , handler);
347    //}
348
349    /// Notify the server the request has been handled.
350    /**
351     * In certain situations (such as a Proactor client using the async
352     * read functions) it will be necessary to call end, rather than 
353     * just returning from the sub_main function.
354     *
355     * @param http_status The HTTP status of the request.
356     *
357     * @param program_status This value is returned to the server
358     * indicating the state of the request after it was finished 
359     * handling. It is implementation defined how the server deals with
360     * this, and it may have no effect on the http status code returned
361     * to the client (eg. 200 OK).
362     *
363     * @returns The value of program_status
364     */
365    int close(common::http::status_code http_status = http::ok
366             , int program_status = 0)
367    {
368      boost::system::error_code ec;
369      this->service.close(this->implementation, http_status,
370          program_status, ec);
371      detail::throw_error(ec);
372      return program_status;
373    }
374
375    int close(common::http::status_code http_status
376             , int program_status
377             , boost::system::error_code& ec)
378    {
379      return this->service.close(this->implementation, http_status
380                                , program_status, ec);
381    }
382
383    /// Reject the request with a '500 Internal Server Error' error.
384    int reject()
385    {
386      this->service.status(this->implementation, aborted);
387      return this->service.close(this->implementation
388                                , http::internal_server_error);
389    }
390
391    /// Abort a request.
392    void abort()
393    {
394      this->service.status(this->implementation, aborted);
395    }
396
397    /// Clear the data for the request, for reusing this object.
398    void clear()
399    {
400      this->service.clear(this->implementation);
401    }
402
403    /// Get the client connection associated with the request
404    /**
405     * You use the client for read/write calls.
406     */
407    client_type& client()
408    {
409      return this->service.client(this->implementation);
410    }
411
412    /// Read some data into the request, parsing if necessary.
413    void read_some()
414    {
415      boost::system::error_code ec;
416      this->service.read_some(this->implementation, ec);
417      detail::throw_error(ec);
418    }
419
420    /// Read some data into the request, parsing if necessary.
421    std::size_t
422    read_some(boost::system::error_code& ec)
423    {
424      return this->service.read_some(this->implementation, ec);
425    }
426
427    /// Read some data into the supplied buffer, parsing if necessary.
428    // **FIXME** (remove - use req.client().read_some() instead)
429    template<typename MutableBufferSequence>
430    void read_some(const MutableBufferSequence& buf)
431    {
432      boost::system::error_code ec;
433      this->service.read_some(this->implementation, buf, ec);
434      detail::throw_error(ec);
435    }
436
437    /// Read some data into the supplied buffer, parsing if necessary.
438    // **FIXME** (remove - use req.client().read_some() instead)
439    template<typename MutableBufferSequence>
440    std::size_t
441    read_some(const MutableBufferSequence& buf
442             , boost::system::error_code& ec)
443    {
444      return this->service.read_some(this->implementation, buf, ec);
445    }
446
447    /////////////////////////////////////////////////////////
448    // Helper-functions for the basic CGI 1.1 meta-variables.
449    /////////////////////////////////////////////////////////
450    
451    string_type& auth_type()
452    { return env["AUTH_TYPE"]; }
453
454    /// Get the content length as a long.
455    /**
456     * The content length defaults to zero if it isn't explicitly set
457     * by your HTTP server.
458     */
459    long content_length()
460    { 
461      string_type& cl(env["CONTENT_LENGTH"]);
462      return boost::lexical_cast<long>(cl.empty() ? "0" : cl); 
463    }
464
465    /// The content type of the request.
466    /**
467     * Common value: text/html.
468     */
469    string_type& content_type()
470    { return env["CONTENT_TYPE"]; }
471
472    /// The protocol used by the server to communicate to the script.
473    /**
474     * Common value: CGI/1.1.
475     */
476    string_type& gateway_interface()
477    { return env["GATEWAY_INTERFACE"]; }
478
479    /// Additional information, appendended to the script.
480    common::path_info path_info()
481    { return env["PATH_INFO"]; }
482
483    /// The translated version of the path info.
484    /**
485     * Your HTTP server may provide this, depending on configuration. 
486     * The path info can represent a resource on the local file system.
487     */
488    string_type& path_translated()
489    { return env["PATH_TRANSLATED"]; }
490
491    /// The query string for the request.
492    /**
493     * This is the part of the request URI after a '?'. A GET request
494     * passes request parameters, URL encoded in the query string.
495     */
496    string_type& query_string()
497    { return env["QUERY_STRING"]; }
498    void set_query_string(string_type const& val)
499    { env["QUERY_STRING"] = val; }
500
501    /// The host address of the remote user.
502    string_type& remote_addr()
503    { return env["REMOTE_ADDR"]; }
504
505    /// The host name of the remote user's machine.
506    string_type& remote_host()
507    { return env["REMOTE_HOST"]; }
508
509    /// The user making the request.
510    string_type& remote_ident()
511    { return env["REMOTE_IDENT"]; }
512
513    /// The userid of the person accessing the script.
514    string_type& remote_user()
515    { return env["REMOTE_USER"]; }
516
517    /// The method of the request.
518    /**
519     * Common values: `GET`, `POST`, `HEAD`.
520     */
521    string_type& method()
522    { return env["REQUEST_METHOD"]; }
523    void set_method(string_type const& val)
524    { env["REQUEST_METHOD"] = val; }
525
526    /// The method of the request (long-hand of `method()`).
527    /**
528     * Common values: `GET`, `POST`, `HEAD`.
529     */
530    string_type& request_method()
531    { return env["REQUEST_METHOD"]; }
532
533    /// Get the URI of the request.
534    string_type& uri()
535    { return env["REQUEST_URI"]; }
536
537    /// Get the URI of the request (long-hand of `uri()`).
538    string_type& request_uri()
539    { return env["REQUEST_URI"]; }
540
541    /// The name of the script.
542    string_type& script_name()
543    { return env["SCRIPT_NAME"]; }
544
545    /// The URL of the script.
546    string_type& script_url()
547    { return env["SCRIPT_URL"]; }
548
549    /// The full URI of the script.
550    string_type& script_uri()
551    { return env["SCRIPT_URI"]; }
552
553    /// Get the name of the server.
554    /**
555     * Usually set in your HTTP configuration. This could be the name
556     * of a virtual host.
557     */
558    string_type& server_name()
559    { return env["SERVER_NAME"]; }
560
561    /// Get the port the calling server is listening on.
562    /**
563     * Common value: 80.
564     */
565    string_type& server_port()
566    { return env["SERVER_PORT"]; }
567
568    /// Get the protocol being used by the calling server.
569    /**
570     * Common value: HTTP/1.1.
571     */
572    string_type& server_protocol()
573    { return env["SERVER_PROTOCOL"]; }
574
575    /// Get a string identifying the calling server.
576    /**
577     * CGI scripts are generally called by a web-facing HTTP server.
578     * When set, this string can be useful for knowing what is calling 
579     * the script, especially in a multi-server or load balanced
580     * environment.
581     */
582    string_type& server_software()
583    { return env["SERVER_SOFTWARE"]; }
584    
585    /// Get the web page the user came from.
586    /**
587     * HTTP equivalent: `HTTP_REFERER`
588     *
589     * The referer is commonly used for tracking user's movements and
590     * origins. For instance, some sites use this to highlight some
591     * keywords on the page when users come from a search engine.
592     *
593     * Note that you cannot ever guarantee on this value being either set
594     * or accurate.
595     */
596    string_type& referer()
597    { return env["HTTP_REFERER"]; }
598    // -- end helper-functions]
599
600    /// Get the charset from the CONTENT_TYPE header
601    string_type charset()
602    {
603      string_type ctype(content_type());
604      std::size_t pos = ctype.find("charset=");
605      string_type val(ctype.substr(pos+8, ctype.find(";", pos)));
606      boost::algorithm::trim(val);
607      return val.empty() ?  BOOST_CGI_DEFAULT_CHARSET : val;
608    }
609    
610    /// The email of the user making the request.
611    string_type& http_from ()
612    { return env["HTTP_FROM"]; }
613
614    /// The cookies sent by the user making the request.
615    string_type& http_cookie ()
616    { return env["HTTP_COOKIE"]; }
617
618    /// The role that the request is playing
619    /**
620     * The default role type is responder.
621     *
622     * In some cases - for instance with FastCGI - the role type can be
623     * different
624     * eg. `authorizer`, or `filter`.
625     */
626    role_type role() const
627    {
628      return this->service.role(this->implementation);
629    }
630
631    /// Get / Set the status of a request.
632    /**
633     * The usual way to set the request status is to set it
634     * when calling `close`.
635     */
636    common::request_status status() const
637    {
638      return this->service.status(this->implementation);
639    }
640    
641    /// Set the status of a request.
642    void status(common::request_status const& status)
643    {
644      this->service.status(this->implementation, status);
645    }
646
647    /// Sets the maximum number of async requests.
648    boost::uint16_t& async_requests()
649    {
650      return this->implementation.async_requests_;
651    }
652
653   };
654
655 } // namespace common
656BOOST_CGI_NAMESPACE_END
657
658#include "boost/cgi/detail/pop_options.hpp"
659
660#endif // CGI_BASIC_REQUEST_HPP_INCLUDED__
661
662/*
663NOTES::future_plans:
664* When a request is aborted (eg. when the client closes the connection)
665  the library can call an abort_handler() function stored in the request.
666  - The user should supply an abort_handler-derived function if they want
667    any special handling of aborted requests
668*/