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_ */