Permalink
Browse files

The request handler will be left in an inconsistent state if Apache d…

…ies during the middle of sending headers. This commit contains the beginning of an attempt to fix that, but it'll probably be easier to drop persistent pipe connections all together.
  • Loading branch information...
1 parent b39e6c0 commit 16e0de8a283d66d69be67997d26cbcbe609b73d2 Hongli Lai committed Feb 3, 2008
View
@@ -11,30 +11,113 @@ namespace Passenger {
using namespace std;
using namespace boost;
-// TODO: write better documentation
+/**
+ * Represents a single instance of a Ruby on Rails application, as spawned by
+ * SpawnManager. An Application can handle one CGI request at a time, through its
+ * two communication channels: reader and writer.
+ *
+ * Application is supposed to be used as follows:
+ * 1. The web server first opens the Application, thereby telling the Application
+ * that the web server wants to establish a new CGI session. Application
+ * will return a lock, which the web server must hold onto.
+ * 2. The web server sends the CGI request data through the Application's
+ * writer channel.
+ * 3. The web server reads the HTTP response from the Application's reader channel,
+ * and forwarding that to the web browser.
+ * 4. The web server destroys the previously obtained lock.
+ * 5. The web server closes the Application, thereby ending the CGI session.
+ *
+ * An Application can be reopened after it has been closed.
+ *
+ * Example:
+ * @code
+ * // Create a new Application.
+ * ApplicationPtr app(some_function_which_returns_an_application());
+ * // Open a new CGI session, and save the lock.
+ * Application::LockPtr lock(app->openSession());
+ *
+ * // Process request and response.
+ * send_cgi_headers_to(app->getWriter());
+ * response = read_response_from(app->getReader());
+ * process_response(response);
+ *
+ * // Now we're done. *First* we destroy the lock!
+ * lock = Application::LockPtr();
+ * // And *then* we close the session.
+ * app->closeSession();
+ * @endcode
+ *
+ * <h2>About open/close
+ */
class Application {
private:
+ struct LockData {
+ bool locked;
+ };
+
string appRoot;
pid_t pid;
int reader, writer;
+ bool opened;
+ shared_ptr<LockData> lockData;
+
public:
+ class Lock {
+ private:
+ friend class Application;
+ shared_ptr<LockData> data;
+
+ Lock(shared_ptr<LockData> data) {
+ this->data = data;
+ }
+
+ public:
+ Lock() {}
+
+ ~Lock() {
+ if (data != NULL) {
+ data->locked = false;
+ }
+ P_TRACE("Unlocked!");
+ }
+ };
+
+ typedef shared_ptr<Lock> LockPtr;
+
Application(const string &theAppRoot, pid_t pid, int reader, int writer) {
appRoot = theAppRoot;
this->pid = pid;
this->reader = reader;
this->writer = writer;
+ opened = false;
+ lockData = ptr(new LockData());
+ lockData->locked = false;
P_TRACE("Application " << this << ": created.");
}
- ~Application() {
- closeReader();
- closeWriter();
+ virtual ~Application() {
+ close(reader);
+ close(writer);
P_TRACE("Application " << this << ": destroyed.");
}
- void detachCommunicationChannels() {
- reader = -1;
- writer = -1;
+ LockPtr openSession() {
+ opened = true;
+ lockData->locked = true;
+ return ptr(new Lock(lockData));
+ }
+
+ void closeSession() {
+ opened = false;
+ P_TRACE("Closed!");
+ }
+
+ bool isOpen() const {
+ return opened;
+ }
+
+ bool hasError() const {
+ return opened && !lockData->locked;
}
string getAppRoot() const {
@@ -52,20 +135,6 @@ class Application {
int getWriter() const {
return writer;
}
-
- void closeReader() {
- if (reader != -1) {
- close(reader);
- reader = -1;
- }
- }
-
- void closeWriter() {
- if (writer != -1) {
- close(writer);
- writer = -1;
- }
- }
};
typedef shared_ptr<Application> ApplicationPtr;
@@ -22,7 +22,8 @@ class ApplicationPool {
public:
virtual ~ApplicationPool() {};
- virtual ApplicationPtr get(const string &appRoot, const string &user = "", const string &group = "") = 0;
+ virtual pair<ApplicationPtr, Application::LockPtr>
+ get(const string &appRoot, const string &user = "", const string &group = "") = 0;
};
// TODO: document this
@@ -39,11 +40,6 @@ class StandardApplicationPool: public ApplicationPool {
mutex lock;
bool threadSafe;
- string normalizePath(const string &path) {
- // TODO
- return path;
- }
-
public:
StandardApplicationPool(const string &spawnManagerCommand,
const string &logFile = "",
@@ -61,8 +57,8 @@ class StandardApplicationPool: public ApplicationPool {
}
// TODO: improve algorithm
- virtual ApplicationPtr get(const string &appRoot, const string &user = "", const string &group = "") {
- string normalizedAppRoot(normalizePath(appRoot));
+ virtual pair<ApplicationPtr, Application::LockPtr>
+ get(const string &appRoot, const string &user = "", const string &group = "") {
ApplicationPtr app;
mutex::scoped_lock l(lock, threadSafe);
@@ -72,8 +68,12 @@ class StandardApplicationPool: public ApplicationPool {
apps[appRoot] = app;
} else {
app = it->second;
+ if (app->hasError()) {
+ app = spawnManager.spawn(appRoot, user, group);
+ apps[appRoot] = app;
+ }
}
- return app;
+ return make_pair(app, app->openSession());
}
};
@@ -29,7 +29,7 @@ static const apr_bucket_type_t bucket_type_dispatcher = {
apr_bucket_copy_notimpl
};
-
+static bool x = false;
class DispatcherBucket {
private:
apr_status_t
@@ -119,6 +119,7 @@ class DispatcherBucket {
public:
ApplicationPtr app;
+ Application::LockPtr lock;
int pipe;
apr_interval_time_t timeout;
@@ -137,10 +138,14 @@ class DispatcherBucket {
}
result = read_chunk_size(chunk_size, current_timeout);
+ if (x) { result = APR_EBADF; x = !x; }
if (result == APR_EOF || (result == APR_SUCCESS && chunk_size == 0)) {
P_TRACE("DispatcherBucket " << this << ": EOF");
b = apr_bucket_immortal_make(b, "", 0);
*str = (const char *) b->data;
+ x = true;
+ lock = Application::LockPtr();
+ app->closeSession();
return APR_SUCCESS;
} else if (result != APR_SUCCESS) {
char buf[1024];
@@ -179,7 +184,8 @@ class DispatcherBucket {
};
apr_bucket *
-dispatcher_bucket_create(apr_pool_t *pool, ApplicationPtr app, apr_interval_time_t timeout, apr_bucket_alloc_t *list) {
+dispatcher_bucket_create(apr_pool_t *pool, ApplicationPtr app, Application::LockPtr lock,
+ apr_interval_time_t timeout, apr_bucket_alloc_t *list) {
apr_bucket *b;
DispatcherBucket *data;
@@ -196,6 +202,7 @@ dispatcher_bucket_create(apr_pool_t *pool, ApplicationPtr app, apr_interval_time
data = new DispatcherBucket();
data->app = app;
+ data->lock = lock;
data->pipe = app->getReader();
data->timeout = timeout;
b->data = data;
@@ -10,7 +10,8 @@
#include <apr_buckets.h>
#include "Application.h"
-apr_bucket *dispatcher_bucket_create(apr_pool_t *pool, Passenger::ApplicationPtr app,
+apr_bucket *dispatcher_bucket_create(apr_pool_t *pool,
+ Passenger::ApplicationPtr app, Passenger::Application::LockPtr lock,
apr_interval_time_t timeout, apr_bucket_alloc_t *list);
#endif /* _DISPATCHER_BUCKET_H_ */
View
@@ -22,7 +22,8 @@
#include "Types.h"
#include "Utils.h"
#include "DispatcherBucket.h"
-#include "ApplicationPoolClientServer.h"
+//#include "ApplicationPoolClientServer.h"
+#include "ApplicationPool.h"
#include "MessageChannel.h"
using namespace std;
@@ -33,7 +34,7 @@ extern "C" module AP_MODULE_DECLARE_DATA rails_module;
class Hooks {
private:
- ApplicationPoolServerPtr applicationPoolServer;
+ //ApplicationPoolServerPtr applicationPoolServer;
ApplicationPoolPtr applicationPool;
RailsConfig *getConfig(request_rec *r) {
@@ -217,17 +218,18 @@ class Hooks {
initDebugging();
P_DEBUG("Initializing mod_passenger.");
ap_add_version_component(pconf, "Phusion_Passenger/" PASSENGER_VERSION);
- const char *spawnManagerCommand = "/home/hongli/Projects/mod_rails/lib/mod_rails/spawn_manager.rb";
- applicationPoolServer = ptr(new ApplicationPoolServer(spawnManagerCommand, "", "production"));
+ //const char *spawnManagerCommand = "/home/hongli/Projects/mod_rails/lib/mod_rails/spawn_manager.rb";
+ //applicationPoolServer = ptr(new ApplicationPoolServer(spawnManagerCommand, "", "production"));
}
~Hooks() {
P_DEBUG("Shutting down mod_passenger.");
}
void initChild(apr_pool_t *pchild, server_rec *s) {
- applicationPool = applicationPoolServer->connect();
- applicationPoolServer->detach();
+ //applicationPool = applicationPoolServer->connect();
+ //applicationPoolServer->detach();
+ applicationPool = ptr(new StandardApplicationPool(""));
}
int handleRequest(request_rec *r) {
@@ -271,12 +273,13 @@ class Hooks {
apr_bucket *b;
P_DEBUG("Processing HTTP request: " << r->uri);
- ApplicationPtr app(applicationPool->get(string(railsDir) + "/.."));
- P_TRACE("Connected to application: reader FD = " << app->getReader() << ", writer FD = " << app->getWriter());
- sendHeaders(r, app->getWriter());
+ pair<ApplicationPtr, Application::LockPtr> p(applicationPool->get(string(railsDir) + "/.."));
+ Application &app(*p.first);
+ P_TRACE("Connected to application: reader FD = " << app.getReader() << ", writer FD = " << app.getWriter());
+ sendHeaders(r, app.getWriter());
bb = apr_brigade_create(r->connection->pool, r->connection->bucket_alloc);
- b = dispatcher_bucket_create(r->pool, app,
+ b = dispatcher_bucket_create(r->pool, p.first, p.second,
r->server->timeout, r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, b);

0 comments on commit 16e0de8

Please sign in to comment.