00001 #ifndef DVCGI_SESSIONSERVER_H 00002 #define DVCGI_SESSIONSERVER_H 00003 // $Id: sessionserver.h,v 1.15 2004/03/20 14:57:35 dvermeir Exp $ 00004 00005 #include <iostream> 00006 00007 #include <dvutil/props.h> 00008 #include <dvnet/usocket.h> 00009 #include <dvnet/userversocket.h> 00010 #include <dvcgi/httphead.h> 00011 /** @file 00012 * Defines SessionServer that allows cgi programs to communicate 00013 * with the browser using stream-like operations. 00014 */ 00015 00016 namespace Dv { 00017 namespace Cgi { 00018 /** A class that packages the link to the hidden server. */ 00019 class SessionServer: public std::ostream { 00020 public: 00021 /** Constructor. 00022 * A Dv::Cgi::SessionServer object must be used in a cgi 00023 * program. 00024 * @code 00025 * Dv::Cgi::SessionServer client("myprog",5*1000, "index.html", "http://tinf2.vub.ac.be/index.html"); 00026 * @endcode 00027 * - If there is a cookie with the name/key "myprog", its 00028 * value will be interpreted as the name of a file 00029 * that corresponds to a unix socket on which a server 00030 * is listening. 00031 * - If a server is listening on that socket, this constructor will simply 00032 * transfer the cgi formdata, cookies and environment to 00033 * the server, then copy any input from the server to 00034 * cout and exit the program. 00035 * - If no server is running, it will start the server by 00036 * forking a daemon and then behave as above. 00037 * The constructor will only return in de server daemon 00038 * process. 00039 * 00040 * The server communicates with its (cgi) clients using 00041 * a unix socket attached to a filename 00042 * <code>/tmp/program_name.xxx</code> 00043 * where <code>xxx</code> is generated to make the filename 00044 * unique. 00045 * 00046 * Application code is rather simple: the program communicates 00047 * with the client (a Dv::Cgi::SessionServer object) using 00048 * interactions of the form 00049 * @code 00050 * client << .. html .. << Dv::Cgi::eof; 00051 * // now client input is available, e.g. in client.props() 00052 * if ( client("number") ) { 00053 * int number; 00054 * client.props() >> "number" >> number; 00055 * .. 00056 * } 00057 * @endcode 00058 * 00059 * Note that all C++ variables are persistent (since they 00060 * reside in the server process). E.g. it is easy to 00061 * write a login function like so: 00062 * @code 00063 * std::string 00064 * login(Dv::Cgi::SessionServer& client) { 00065 * while (client) { 00066 * client << login_form << Dv::Cgi::eof; 00067 * if ( client("name") ) { 00068 * std::string uname = client.props("name"); 00069 * std::string uname = client.props("password"); 00070 * if (check_password_valid) 00071 * return uname; 00072 * } 00073 * } 00074 * throw std::runtime_error("login timed out"); 00075 * } 00076 * @endcode 00077 * 00078 * To exit the application use Dv::Cgi::SessionServer::disconnect . 00079 * 00080 * Silly example applications are available in the distribution: see 00081 * test-sessionserver.C and example-session.C . 00082 * 00083 * @param program_name used to generate the server unix server socket 00084 * filename, also used as the name of the cookie whose value 00085 * is the actual server filename. 00086 * @param delay (in milliseconds) is used by the client and the 00087 * server to determine the allowable wait for response. 00088 * @param timeouturl is used to relocate any request that 00089 * suffers from a timeout 00090 * @param errorurl is used to relocate any request that 00091 * suffers from an unrecoverable error (except for timeout) 00092 * @param logdir directory where logfile will be created 00093 * @param sockdir directory where sockets will be created 00094 * @warning do not set a cookie with key/name program_name 00095 */ 00096 SessionServer(const std::string& program_name, time_t delay, 00097 const std::string& timeouturl, const std::string& errorurl, 00098 const std::string& logdir="/tmp", const std::string& sockdir="/tmp"); 00099 /** Destructor. */ 00100 virtual ~SessionServer(); 00101 00102 /** Name of server application. */ 00103 const std::string& name() const { return name_; } 00104 00105 /** Get props object containing formdata from client. */ 00106 const Util::Props& props() const { return props_; } 00107 /** Get props object containing formdata from client. */ 00108 Dv::Util::Props& props() { return props_; } 00109 00110 /** Easy check for presence of a formdata item. */ 00111 const std::string* operator()(const std::string& key) const { return props().find(key); } 00112 /** @return value of form data variable 00113 * @exception std::runtime_error if variable is not defined 00114 */ 00115 const std::string& props(const std::string& name) const throw (std::runtime_error) { 00116 return props()[name].str(); } 00117 /** @return value of form data variable 00118 * @exception std::runtime_error if variable is not defined 00119 */ 00120 std::string& props(const std::string& name) throw (std::runtime_error) { return props()[name].str(); } 00121 00122 /** Get Props object containing shell environment from client. */ 00123 const Util::Props& env() const { return env_; } 00124 /** @return value of environment variable. 00125 * @exception std::runtime_error if variable is not defined 00126 */ 00127 const std::string& env(const std::string& name) const throw (std::runtime_error) { 00128 return (env()[name]).str() ; } 00129 00130 /** Get Props object containing cookies from client. */ 00131 const Util::Props& cookies() const { return cookies_; } 00132 /** @return value of cookie variable 00133 * @exception std::runtime_error if variable is not defined 00134 */ 00135 const std::string& cookies(const std::string& name) const throw (std::exception) { 00136 return (cookies()[name]).str() ; } 00137 00138 /** Return url of current request. 00139 * @return value of REQUEST_URI in env() 00140 * @exception std::runtime_error if not available 00141 * @sa Dv::Cgi::SessionServer::env 00142 */ 00143 std::string here() const throw (std::runtime_error); 00144 /** Return base url of current request. 00145 * @return value of here() without parameters, i.e. 00146 * after removing the first '?' and anything following it. 00147 * @exception std::runtime_error if not available 00148 * @sa Dv::Cgi::SessionServer::here 00149 */ 00150 std::string here_base() const throw (std::runtime_error); 00151 00152 /** Get HttpHeader object containing output headers. */ 00153 const HttpHeader& header() const { return header_; } 00154 /** Get HttpHeader object containing output headers. */ 00155 HttpHeader& header() { return header_; } 00156 /** Get HttpHeader object containing output headers. 00157 * @deprecated Use <code>session.header().f()</code> instead of 00158 * <code>session->f()</code>. 00159 */ 00160 HttpHeader* operator->() { return &header_; } 00161 00162 /** Get status of server. 00163 * @retval true iff server is working 00164 * @retval false otherwise 00165 */ 00166 operator bool() const { return status_; } 00167 00168 /** Remove cookie, relocate to url, stop network server and connection. 00169 * @param url to relocate to 00170 */ 00171 void disconnect(const std::string& url); 00172 /** Stop server and network connection. */ 00173 void disconnect(); 00174 /** Refresh: send header (if not already sent) and get input from client. */ 00175 void refresh(); 00176 00177 /** Log stream. 00178 * This is a Dv::Util::logstream: each line is automatically prefixed by 00179 * the date and the identification of the process. 00180 * @sa Dv::Cgi::SessionServer::name 00181 */ 00182 std::ostream& log() const { return *log_; } 00183 00184 private: 00185 SessionServer(const SessionServer& s); 00186 SessionServer& operator=(const SessionServer& s); 00187 00188 /** Filter class that ensures that header_ is output before 00189 * anything else. */ 00190 class Filter { 00191 public: 00192 Filter(SessionServer& ss); 00193 int get(); 00194 int put(int c); 00195 int sync(); 00196 int close() { return 0; } 00197 int count() const { return count_; } 00198 void clear() { count_ = 0; } 00199 private: 00200 unsigned int count_; 00201 SessionServer& server_; 00202 }; 00203 friend class Filter; 00204 00205 void send_header(); 00206 void get_input(); 00207 00208 std::string name_; 00209 time_t delay_; 00210 std::string timeouturl_; 00211 std::string errorurl_; 00212 std::string server_filename_; 00213 00214 Dv::Net::userversocket* server_socket_; 00215 Dv::Net::usocket* client_socket_; 00216 bool status_; 00217 00218 Dv::Util::Props props_; 00219 Dv::Util::Props env_; 00220 Dv::Util::Props cookies_; 00221 00222 Dv::Cgi::HttpHeader header_; 00223 00224 std::ostream* log_; 00225 Filter filter_; 00226 }; 00227 00228 /** Convenience function, simply calls refresh on its argument. 00229 * 00230 * Example: 00231 * @code 00232 * Dv::Cgi::SessionServer s(..); 00233 * 00234 * s.header().content_type("text/html").no_cache(); 00235 * 00236 * while (s) { 00237 * Dv::Util::Props& input(s.props()); 00238 * 00239 * s << "<html>..</html>" << Dv::Cgi::eof; 00240 * } 00241 * @endcode 00242 * @sa operator<<(std::ostream& os, void (*f)(Dv::Cgi::SessionServer&) 00243 */ 00244 void eof(SessionServer&); 00245 }} 00246 00247 /** Overloading for SessionServer manipulation. 00248 * @warning Uses <code>dynamic_cast<SessionServer*>(os)</code> internally. 00249 * @sa Dv::Cgi::eof 00250 */ 00251 void operator<<(std::ostream& os,void (*f)(Dv::Cgi::SessionServer&)); 00252 #endif
dvcgi-0.5.14 | [22 January, 2006] |