Application.h

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

Generated on Tue Apr 29 15:25:32 2008 for Passenger by  doxygen 1.5.3