- Main Page
- Modules
- Classes
- Files

- File List

# DirectoryMapper.h

    00001 /*
    00002  *  Phusion Passenger - http://www.modrails.com/
    00003  *  Copyright (c) 2008, 2009 Phusion
    00004  *
    00005  *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
    00006  *
    00007  *  Permission is hereby granted, free of charge, to any person obtaining a copy
    00008  *  of this software and associated documentation files (the "Software"), to deal
    00009  *  in the Software without restriction, including without limitation the rights
    00010  *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    00011  *  copies of the Software, and to permit persons to whom the Software is
    00012  *  furnished to do so, subject to the following conditions:
    00013  *
    00014  *  The above copyright notice and this permission notice shall be included in
    00015  *  all copies or substantial portions of the Software.
    00016  *
    00017  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    00018  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    00019  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    00020  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    00021  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    00022  *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    00023  *  THE SOFTWARE.
    00024  */
    00025 #ifndef _PASSENGER_DIRECTORY_MAPPER_H_
    00026 #define _PASSENGER_DIRECTORY_MAPPER_H_
    00027
    00028 #include <string>
    00029 #include <set>
    00030 #include <cstring>
    00031
    00032 #include <oxt/backtrace.hpp>
    00033
    00034 #include "CachedFileStat.hpp"
    00035 #include "Configuration.h"
    00036 #include "Utils.h"
    00037
    00038 // The Apache/APR headers *must* come after the Boost headers, otherwise
    00039 // compilation will fail on OpenBSD.
    00040 #include <httpd.h>
    00041 #include <http_core.h>
    00042
    00043 namespace Passenger {
    00044
    00045 using namespace std;
    00046 using namespace oxt;
    00047
    00048 /**
    00049  * Utility class for determining URI-to-application directory mappings.
    00050  * Given a URI, it will determine whether that URI belongs to a Phusion
    00051  * Passenger-handled application, what the base URI of that application is,
    00052  * and what the associated 'public' directory is.
    00053  *
    00054  * @note This class is not thread-safe, but is reentrant.
    00055  * @ingroup Core
    00056  */
    00057 class DirectoryMapper {
    00058 public:
    00059         enum ApplicationType {
    00060                 NONE,
    00061                 RAILS,
    00062                 RACK,
    00063                 WSGI
    00064         };
    00065
    00066 private:
    00067         DirConfig *config;
    00068         request_rec *r;
    00069         CachedFileStat *cstat;
    00070         unsigned int throttleRate;
    00071         bool baseURIKnown;
    00072         const char *baseURI;
    00073         ApplicationType appType;
    00074
    00075         inline bool shouldAutoDetectRails() {
    00076                 return config->autoDetectRails == DirConfig::ENABLED ||
    00077                         config->autoDetectRails == DirConfig::UNSET;
    00078         }
    00079
    00080         inline bool shouldAutoDetectRack() {
    00081                 return config->autoDetectRack == DirConfig::ENABLED ||
    00082                         config->autoDetectRack == DirConfig::UNSET;
    00083         }
    00084
    00085         inline bool shouldAutoDetectWSGI() {
    00086                 return config->autoDetectWSGI == DirConfig::ENABLED ||
    00087                         config->autoDetectWSGI == DirConfig::UNSET;
    00088         }
    00089
    00090 public:
    00091         /**
    00092          * Create a new DirectoryMapper object.
    00093          *
    00094          * @param cstat A CachedFileStat object used for statting files.
    00095          * @param throttleRate A throttling rate for cstat.
    00096          * @warning Do not use this object after the destruction of <tt>r</tt>,
    00097          *          <tt>config</tt> or <tt>cstat</tt>.
    00098          */
    00099         DirectoryMapper(request_rec *r, DirConfig *config,
    00100                         CachedFileStat *cstat, unsigned int throttleRate) {
    00101                 this->r = r;
    00102                 this->config = config;
    00103                 this->cstat = cstat;
    00104                 this->throttleRate = throttleRate;
    00105                 appType = NONE;
    00106                 baseURIKnown = false;
    00107                 baseURI = NULL;
    00108         }
    00109
    00110         /**
    00111          * Determine whether the given HTTP request falls under one of the specified
    00112          * RailsBaseURIs or RackBaseURIs. If yes, then the first matching base URI will
    00113          * be returned.
    00114          *
    00115          * If Rails/Rack autodetection was enabled in the configuration, and the document
    00116          * root seems to be a valid Rails/Rack 'public' folder, then this method will
    00117          * return "/".
    00118          *
    00119          * Otherwise, NULL will be returned.
    00120          *
    00121          * @throws FileSystemException This method might also examine the filesystem in
    00122          *            order to detect the application's type. During that process, a
    00123          *            FileSystemException might be thrown.
    00124          * @warning The return value may only be used as long as <tt>config</tt>
    00125          *          hasn't been destroyed.
    00126          */
    00127         const char *getBaseURI() {
    00128                 TRACE_POINT();
    00129                 if (baseURIKnown) {
    00130                         return baseURI;
    00131                 }
    00132
    00133                 set<string>::const_iterator it;
    00134                 const char *uri = r->uri;
    00135                 size_t uri_len = strlen(uri);
    00136
    00137                 if (uri_len == 0 || uri[0] != '/') {
    00138                         baseURIKnown = true;
    00139                         return NULL;
    00140                 }
    00141
    00142                 UPDATE_TRACE_POINT();
    00143                 for (it = config->railsBaseURIs.begin(); it != config->railsBaseURIs.end(); it++) {
    00144                         const string &base(*it);
    00145                         if (  base == "/"
    00146                          || ( uri_len == base.size() && memcmp(uri, base.c_str(), uri_len) == 0 )
    00147                          || ( uri_len  > base.size() && memcmp(uri, base.c_str(), base.size()) == 0
    00148                                                      && uri[base.size()] == '/' )
    00149                         ) {
    00150                                 baseURIKnown = true;
    00151                                 baseURI = base.c_str();
    00152                                 appType = RAILS;
    00153                                 return baseURI;
    00154                         }
    00155                 }
    00156
    00157                 UPDATE_TRACE_POINT();
    00158                 for (it = config->rackBaseURIs.begin(); it != config->rackBaseURIs.end(); it++) {
    00159                         const string &base(*it);
    00160                         if (  base == "/"
    00161                          || ( uri_len == base.size() && memcmp(uri, base.c_str(), uri_len) == 0 )
    00162                          || ( uri_len  > base.size() && memcmp(uri, base.c_str(), base.size()) == 0
    00163                                                      && uri[base.size()] == '/' )
    00164                         ) {
    00165                                 baseURIKnown = true;
    00166                                 baseURI = base.c_str();
    00167                                 appType = RACK;
    00168                                 return baseURI;
    00169                         }
    00170                 }
    00171
    00172                 UPDATE_TRACE_POINT();
    00173                 if (shouldAutoDetectRails()
    00174                  && verifyRailsDir(config->getAppRoot(ap_document_root(r)), cstat, throttleRate)) {
    00175                         baseURIKnown = true;
    00176                         baseURI = "/";
    00177                         appType = RAILS;
    00178                         return baseURI;
    00179                 }
    00180
    00181                 UPDATE_TRACE_POINT();
    00182                 if (shouldAutoDetectRack()
    00183                  && verifyRackDir(config->getAppRoot(ap_document_root(r)), cstat, throttleRate)) {
    00184                         baseURIKnown = true;
    00185                         baseURI = "/";
    00186                         appType = RACK;
    00187                         return baseURI;
    00188                 }
    00189
    00190                 UPDATE_TRACE_POINT();
    00191                 if (shouldAutoDetectWSGI()
    00192                  && verifyWSGIDir(config->getAppRoot(ap_document_root(r)), cstat, throttleRate)) {
    00193                         baseURIKnown = true;
    00194                         baseURI = "/";
    00195                         appType = WSGI;
    00196                         return baseURI;
    00197                 }
    00198
    00199                 baseURIKnown = true;
    00200                 return NULL;
    00201         }
    00202
    00203         /**
    00204          * Returns the filename of the 'public' directory of the Rails/Rack application
    00205          * that's associated with the HTTP request.
    00206          *
    00207          * Returns an empty string if the document root of the HTTP request
    00208          * cannot be determined, or if it isn't a valid folder.
    00209          *
    00210          * @throws FileSystemException An error occured while examening the filesystem.
    00211          */
    00212         string getPublicDirectory() {
    00213                 if (!baseURIKnown) {
    00214                         getBaseURI();
    00215                 }
    00216                 if (baseURI == NULL) {
    00217                         return "";
    00218                 }
    00219
    00220                 const char *docRoot = ap_document_root(r);
    00221                 size_t len = strlen(docRoot);
    00222                 if (len > 0) {
    00223                         string path;
    00224                         if (docRoot[len - 1] == '/') {
    00225                                 path.assign(docRoot, len - 1);
    00226                         } else {
    00227                                 path.assign(docRoot, len);
    00228                         }
    00229                         if (strcmp(baseURI, "/") != 0) {
    00230                                 /* Application is deployed in a sub-URI.
    00231                                  * This is probably a symlink, so let's resolve it.
    00232                                  */
    00233                                 path.append(baseURI);
    00234                                 path = resolveSymlink(path);
    00235                         }
    00236                         return path;
    00237                 } else {
    00238                         return "";
    00239                 }
    00240         }
    00241
    00242         /**
    00243          * Returns the application type that's associated with the HTTP request.
    00244          *
    00245          * @throws FileSystemException An error occured while examening the filesystem.
    00246          */
    00247         ApplicationType getApplicationType() {
    00248                 if (!baseURIKnown) {
    00249                         getBaseURI();
    00250                 }
    00251                 return appType;
    00252         }
    00253
    00254         /**
    00255          * Returns the application type (as a string) that's associated
    00256          * with the HTTP request.
    00257          *
    00258          * @throws FileSystemException An error occured while examening the filesystem.
    00259          */
    00260         const char *getApplicationTypeString() {
    00261                 if (!baseURIKnown) {
    00262                         getBaseURI();
    00263                 }
    00264                 switch (appType) {
    00265                 case RAILS:
    00266                         return "rails";
    00267                 case RACK:
    00268                         return "rack";
    00269                 case WSGI:
    00270                         return "wsgi";
    00271                 default:
    00272                         return NULL;
    00273                 };
    00274         }
    00275
    00276         /**
    00277          * Returns the environment under which the application should be spawned.
    00278          *
    00279          * @throws FileSystemException An error occured while examening the filesystem.
    00280          */
    00281         const char *getEnvironment() {
    00282                 switch (getApplicationType()) {
    00283                 case RAILS:
    00284                         return config->getRailsEnv();
    00285                 case RACK:
    00286                         return config->getRackEnv();
    00287                 default:
    00288                         return "production";
    00289                 }
    00290         }
    00291 };
    00292
    00293 } // namespace Passenger
    00294
    00295 #endif /* _PASSENGER_DIRECTORY_MAPPER_H_ */
    00296