Application.h

00001 /*
00002  *  Phusion Passenger - http://www.modrails.com/
00003  *  Copyright (C) 2008  Phusion
00004  *
00005  *  Phusion Passenger is a trademark of Hongli Lai & Ninh Bui.
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; version 2 of the License.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00019  */
00020 #ifndef _PASSENGER_APPLICATION_H_
00021 #define _PASSENGER_APPLICATION_H_
00022 
00023 #include <boost/shared_ptr.hpp>
00024 #include <boost/function.hpp>
00025 #include <string>
00026 
00027 #include <sys/types.h>
00028 #include <sys/socket.h>
00029 #include <sys/un.h>
00030 #include <unistd.h>
00031 #include <errno.h>
00032 #include <ctime>
00033 #include <cstring>
00034 
00035 #include "MessageChannel.h"
00036 #include "Exceptions.h"
00037 #include "Logging.h"
00038 
00039 namespace Passenger {
00040 
00041 using namespace std;
00042 using namespace boost;
00043 
00044 /**
00045  * Represents a single Ruby on Rails or Rack application instance.
00046  *
00047  * @ingroup Support
00048  */
00049 class Application {
00050 public:
00051         class Session;
00052         /** Convenient alias for Session smart pointer. */
00053         typedef shared_ptr<Session> SessionPtr;
00054         
00055         /**
00056          * Represents the life time of a single request/response pair of a
00057          * Ruby on Rails or Rack application.
00058          *
00059          * Session is used to forward a single HTTP request to a Ruby on Rails/Rack
00060          * application. A Session has two communication channels: one for reading data
00061          * from the application, and one for writing data to the application.
00062          *
00063          * In general, a session object is to be used in the following manner:
00064          *
00065          *  -# Convert the HTTP request headers into a string, as expected by sendHeaders().
00066          *     Then send that string by calling sendHeaders().
00067          *  -# In case of a POST of PUT request, send the HTTP request body by calling
00068          *     sendBodyBlock(), possibly multiple times.
00069          *  -# Shutdown the writer channel since you're now done sending data.
00070          *  -# The HTTP response can now be read through the reader channel (getStream()).
00071          *  -# When the HTTP response has been read, the session must be closed.
00072          *     This is done by destroying the Session object.
00073          *
00074          * A usage example is shown in Application::connect(). 
00075          */
00076         class Session {
00077         public:
00078                 /**
00079                  * Implementing classes might throw arbitrary exceptions.
00080                  */
00081                 virtual ~Session() {}
00082                 
00083                 /**
00084                  * Send HTTP request headers to the application. The HTTP headers must be
00085                  * converted into CGI headers, and then encoded into a string that matches this grammar:
00086                  *
00087                    @verbatim
00088                    headers ::= header*
00089                    header ::= name NUL value NUL
00090                    name ::= notnull+
00091                    value ::= notnull+
00092                    notnull ::= "\x01" | "\x02" | "\x02" | ... | "\xFF"
00093                    NUL = "\x00"
00094                    @endverbatim
00095                  *
00096                  * This method should be the first one to be called during the lifetime of a Session
00097                  * object. Otherwise strange things may happen.
00098                  *
00099                  * @param headers The HTTP request headers, converted into CGI headers and encoded as
00100                  *                a string, according to the description.
00101                  * @param size The size, in bytes, of <tt>headers</tt>.
00102                  * @pre headers != NULL
00103                  * @throws IOException The writer channel has already been closed.
00104                  * @throws SystemException Something went wrong during writing.
00105                  * @throws boost::thread_interrupted
00106                  */
00107                 virtual void sendHeaders(const char *headers, unsigned int size) {
00108                         int stream = getStream();
00109                         if (stream == -1) {
00110                                 throw IOException("Cannot write headers to the request handler "
00111                                         "because the writer stream has already been closed.");
00112                         }
00113                         try {
00114                                 MessageChannel(stream).writeScalar(headers, size);
00115                         } catch (const SystemException &e) {
00116                                 throw SystemException("An error occured while writing headers "
00117                                         "to the request handler", e.code());
00118                         }
00119                 }
00120                 
00121                 /**
00122                  * Convenience shortcut for sendHeaders(const char *, unsigned int)
00123                  * @param headers
00124                  * @throws IOException The writer channel has already been closed.
00125                  * @throws SystemException Something went wrong during writing.
00126                  * @throws boost::thread_interrupted
00127                  */
00128                 virtual void sendHeaders(const string &headers) {
00129                         sendHeaders(headers.c_str(), headers.size());
00130                 }
00131                 
00132                 /**
00133                  * Send a chunk of HTTP request body data to the application.
00134                  * You can call this method as many times as is required to transfer
00135                  * the entire HTTP request body.
00136                  *
00137                  * This method should only be called after a sendHeaders(). Otherwise
00138                  * strange things may happen.
00139                  *
00140                  * @param block A block of HTTP request body data to send.
00141                  * @param size The size, in bytes, of <tt>block</tt>.
00142                  * @throws IOException The writer channel has already been closed.
00143                  * @throws SystemException Something went wrong during writing.
00144                  * @throws boost::thread_interrupted
00145                  */
00146                 virtual void sendBodyBlock(const char *block, unsigned int size) {
00147                         int stream = getStream();
00148                         if (stream == -1) {
00149                                 throw IOException("Cannot write request body block to the "
00150                                         "request handler because the writer stream has "
00151                                         "already been closed.");
00152                         }
00153                         try {
00154                                 MessageChannel(stream).writeRaw(block, size);
00155                         } catch (const SystemException &e) {
00156                                 throw SystemException("An error occured while sending the "
00157                                         "request body to the request handler", e.code());
00158                         }
00159                 }
00160                 
00161                 /**
00162                  * Get the I/O stream's file descriptor. This steam is full-duplex,
00163                  * and will be automatically closed upon Session's destruction,
00164                  * unless discardStream() is called.
00165                  *
00166                  * @pre The stream has not been fully closed.
00167                  */
00168                 virtual int getStream() const = 0;
00169                 
00170                 /**
00171                  * Indicate that we don't want to read data anymore from the I/O stream.
00172                  * Calling this method after closeStream() is called will have no effect.
00173                  *
00174                  * @throws SystemException Something went wrong.
00175                  * @throws boost::thread_interrupted
00176                  */
00177                 virtual void shutdownReader() = 0;
00178                 
00179                 /**
00180                  * Indicate that we don't want to write data anymore to the I/O stream.
00181                  * Calling this method after closeStream() is called will have no effect.
00182                  *
00183                  * @throws SystemException Something went wrong.
00184                  * @throws boost::thread_interrupted
00185                  */
00186                 virtual void shutdownWriter() = 0;
00187                 
00188                 /**
00189                  * Close the I/O stream.
00190                  *
00191                  * @throws SystemException Something went wrong.
00192                  * @throws boost::thread_interrupted
00193                  */
00194                 virtual void closeStream() = 0;
00195                 
00196                 /**
00197                  * Discard the I/O stream's file descriptor, so that Session won't automatically
00198                  * closed it upon Session's destruction.
00199                  */
00200                 virtual void discardStream() = 0;
00201                 
00202                 /**
00203                  * Get the process ID of the application instance that belongs to this session.
00204                  */
00205                 virtual pid_t getPid() const = 0;
00206         };
00207 
00208 private:
00209         /**
00210          * A "standard" implementation of Session.
00211          */
00212         class StandardSession: public Session {
00213         protected:
00214                 function<void()> closeCallback;
00215                 int fd;
00216                 pid_t pid;
00217                 
00218         public:
00219                 StandardSession(pid_t pid,
00220                                 const function<void()> &closeCallback,
00221                                 int fd) {
00222                         this->pid = pid;
00223                         this->closeCallback = closeCallback;
00224                         this->fd = fd;
00225                 }
00226         
00227                 virtual ~StandardSession() {
00228                         closeStream();
00229                         closeCallback();
00230                 }
00231                 
00232                 virtual int getStream() const {
00233                         return fd;
00234                 }
00235                 
00236                 virtual void shutdownReader() {
00237                         if (fd != -1) {
00238                                 int ret = InterruptableCalls::shutdown(fd, SHUT_RD);
00239                                 if (ret == -1) {
00240                                         throw SystemException("Cannot shutdown the writer stream",
00241                                                 errno);
00242                                 }
00243                         }
00244                 }
00245                 
00246                 virtual void shutdownWriter() {
00247                         if (fd != -1) {
00248                                 int ret = InterruptableCalls::shutdown(fd, SHUT_WR);
00249                                 if (ret == -1) {
00250                                         throw SystemException("Cannot shutdown the writer stream",
00251                                                 errno);
00252                                 }
00253                         }
00254                 }
00255                 
00256                 virtual void closeStream() {
00257                         if (fd != -1) {
00258                                 int ret = InterruptableCalls::close(fd);
00259                                 if (ret == -1) {
00260                                         throw SystemException("Cannot close the session stream",
00261                                                 errno);
00262                                 }
00263                                 fd = -1;
00264                         }
00265                 }
00266                 
00267                 virtual void discardStream() {
00268                         fd = -1;
00269                 }
00270                 
00271                 virtual pid_t getPid() const {
00272                         return pid;
00273                 }
00274         };
00275 
00276         string appRoot;
00277         pid_t pid;
00278         string listenSocketName;
00279         bool usingAbstractNamespace;
00280         int ownerPipe;
00281 
00282 public:
00283         /**
00284          * Construct a new Application object.
00285          *
00286          * @param theAppRoot The application root of an application. In case of a Rails application,
00287          *             this is the folder that contains 'app/', 'public/', 'config/', etc.
00288          *             This must be a valid directory, but the path does not have to be absolute.
00289          * @param pid The process ID of this application instance.
00290          * @param listenSocketName The name of the listener socket of this application instance.
00291          * @param usingAbstractNamespace Whether <tt>listenSocketName</tt> refers to a Unix
00292          *        socket on the abstract namespace. Note that listenSocketName must not
00293          *        contain the leading null byte, even if it's an abstract namespace socket.
00294          * @param ownerPipe The owner pipe of this application instance.
00295          * @post getAppRoot() == theAppRoot && getPid() == pid
00296          */
00297         Application(const string &theAppRoot, pid_t pid, const string &listenSocketName,
00298                     bool usingAbstractNamespace, int ownerPipe) {
00299                 appRoot = theAppRoot;
00300                 this->pid = pid;
00301                 this->listenSocketName = listenSocketName;
00302                 this->usingAbstractNamespace = usingAbstractNamespace;
00303                 this->ownerPipe = ownerPipe;
00304                 P_TRACE(3, "Application " << this << ": created.");
00305         }
00306         
00307         virtual ~Application() {
00308                 int ret;
00309                 
00310                 if (ownerPipe != -1) {
00311                         do {
00312                                 ret = close(ownerPipe);
00313                         } while (ret == -1 && errno == EINTR);
00314                 }
00315                 if (!usingAbstractNamespace) {
00316                         do {
00317                                 ret = unlink(listenSocketName.c_str());
00318                         } while (ret == -1 && errno == EINTR);
00319                 }
00320                 P_TRACE(3, "Application " << this << ": destroyed.");
00321         }
00322         
00323         /**
00324          * Returns the application root for this application. See the constructor
00325          * for information about the application root.
00326          */
00327         string getAppRoot() const {
00328                 return appRoot;
00329         }
00330         
00331         /**
00332          * Returns the process ID of this application instance.
00333          */
00334         pid_t getPid() const {
00335                 return pid;
00336         }
00337         
00338         /**
00339          * Connect to this application instance with the purpose of sending
00340          * a request to the application. Once connected, a new session will
00341          * be opened. This session represents the life time of a single
00342          * request/response pair, and can be used to send the request
00343          * data to the application instance, as well as receiving the response
00344          * data.
00345          *
00346          * The use of connect() is demonstrated in the following example.
00347          * @code
00348          *   // Connect to the application and get the newly opened session.
00349          *   Application::SessionPtr session(app->connect("/home/webapps/foo"));
00350          *   
00351          *   // Send the request headers and request body data.
00352          *   session->sendHeaders(...);
00353          *   session->sendBodyBlock(...);
00354          *   // Done sending data, so we close the writer channel.
00355          *   session->closeWriter();
00356          *
00357          *   // Now read the HTTP response.
00358          *   string responseData = readAllDataFromSocket(session->getReader());
00359          *   // Done reading data, so we close the reader channel.
00360          *   session->closeReader();
00361          *
00362          *   // This session has now finished, so we close the session by resetting
00363          *   // the smart pointer to NULL (thereby destroying the Session object).
00364          *   session.reset();
00365          *
00366          *   // We can connect to an Application multiple times. Just make sure
00367          *   // the previous session is closed.
00368          *   session = app->connect("/home/webapps/bar")
00369          * @endcode
00370          *
00371          * Note that a RoR application instance can only process one
00372          * request at the same time, and thus only one session at the same time.
00373          * It's unspecified whether Rack applications can handle multiple simultanous sessions.
00374          *
00375          * You <b>must</b> close a session when you no longer need if. If you
00376          * call connect() without having properly closed a previous session,
00377          * you might cause a deadlock because the application instance may be
00378          * waiting for you to close the previous session.
00379          *
00380          * @return A smart pointer to a Session object, which represents the created session.
00381          * @param closeCallback A function which will be called when the session has been closed.
00382          * @post this->getSessions() == old->getSessions() + 1
00383          * @throws SystemException Something went wrong during the connection process.
00384          * @throws IOException Something went wrong during the connection process.
00385          */
00386         SessionPtr connect(const function<void()> &closeCallback) const {
00387                 int fd, ret;
00388                 
00389                 do {
00390                         fd = socket(PF_UNIX, SOCK_STREAM, 0);
00391                 } while (fd == -1 && errno == EINTR);
00392                 if (fd == -1) {
00393                         throw SystemException("Cannot create a new unconnected Unix socket", errno);
00394                 }
00395                 
00396                 struct sockaddr_un addr;
00397                 addr.sun_family = AF_UNIX;
00398                 if (usingAbstractNamespace) {
00399                         strncpy(addr.sun_path + 1, listenSocketName.c_str(), sizeof(addr.sun_path) - 1);
00400                         addr.sun_path[0] = '\0';
00401                 } else {
00402                         strncpy(addr.sun_path, listenSocketName.c_str(), sizeof(addr.sun_path));
00403                 }
00404                 addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
00405                 do {
00406                         ret = ::connect(fd, (const sockaddr *) &addr, sizeof(addr));
00407                 } while (ret == -1 && errno == EINTR);
00408                 if (ret == -1) {
00409                         int e = errno;
00410                         string message("Cannot connect to Unix socket '");
00411                         message.append(listenSocketName);
00412                         message.append("' on the abstract namespace");
00413                         throw SystemException(message, e);
00414                 }
00415                 
00416                 return ptr(new StandardSession(pid, closeCallback, fd));
00417         }
00418 };
00419 
00420 /** Convenient alias for Application smart pointer. */
00421 typedef shared_ptr<Application> ApplicationPtr;
00422 
00423 } // namespace Passenger
00424 
00425 #endif /* _PASSENGER_APPLICATION_H_ */

Generated on Fri Jan 23 08:28:57 2009 for Passenger by  doxygen 1.4.7