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