- Main Page
- Modules
- Namespaces
- Classes
- Files

- File List

# MessageClient.h

    00001 /*
    00002  *  Phusion Passenger - http://www.modrails.com/
    00003  *  Copyright (c) 2010 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_MESSAGE_CLIENT_H_
    00026 #define _PASSENGER_MESSAGE_CLIENT_H_
    00027
    00028 #include <boost/shared_ptr.hpp>
    00029 #include <string>
    00030
    00031 #include "StaticString.h"
    00032 #include "MessageChannel.h"
    00033 #include "Exceptions.h"
    00034 #include "Utils.h"
    00035
    00036
    00037 namespace Passenger {
    00038
    00039 using namespace std;
    00040 using namespace boost;
    00041
    00042 class MessageClient {
    00043 protected:
    00044         FileDescriptor fd;
    00045         MessageChannel channel;
    00046
    00047         /* sendUsername() and sendPassword() exist and are virtual in order to facilitate unit testing. */
    00048
    00049         virtual void sendUsername(MessageChannel &channel, const string &username) {
    00050                 channel.writeScalar(username);
    00051         }
    00052
    00053         virtual void sendPassword(MessageChannel &channel, const StaticString &userSuppliedPassword) {
    00054                 channel.writeScalar(userSuppliedPassword.c_str(), userSuppliedPassword.size());
    00055         }
    00056
    00057         /**
    00058          * Authenticate to the server with the given username and password.
    00059          *
    00060          * @throws SystemException An error occurred while reading data from or sending data to the server.
    00061          * @throws IOException An error occurred while reading data from or sending data to the server.
    00062          * @throws SecurityException The server denied authentication.
    00063          * @throws boost::thread_interrupted
    00064          * @pre <tt>channel</tt> is connected.
    00065          */
    00066         void authenticate(const string &username, const StaticString &userSuppliedPassword) {
    00067                 vector<string> args;
    00068
    00069                 sendUsername(channel, username);
    00070                 sendPassword(channel, userSuppliedPassword);
    00071
    00072                 if (!channel.read(args)) {
    00073                         throw IOException("The ApplicationPool server did not send an authentication response.");
    00074                 } else if (args.size() != 1) {
    00075                         throw IOException("The authentication response that the ApplicationPool server sent is not valid.");
    00076                 } else if (args[0] != "ok") {
    00077                         throw SecurityException("The ApplicationPool server denied authentication: " + args[0]);
    00078                 }
    00079         }
    00080
    00081         /** Closes the connection without throwing close exceptions. */
    00082         void silentDisconnect() {
    00083                 fd = FileDescriptor();
    00084                 channel = MessageChannel();
    00085         }
    00086
    00087 public:
    00088         /**
    00089          * Create a new MessageClient object. It doesn't actually connect to the server until
    00090          * you call connect().
    00091          */
    00092         MessageClient() {
    00093                 /* The reason why we don't connect right away is because we want to make
    00094                  * certain methods virtual for unit testing purposes. We can't call
    00095                  * virtual methods in the constructor. :-(
    00096                  */
    00097         }
    00098
    00099         virtual ~MessageClient() { }
    00100
    00101         /**
    00102          * Connect to the given MessageServer. If a connection was already established,
    00103          * then the old connection will be closed and a new connection will be established.
    00104          *
    00105          * If this MessageClient was in a connected state, and this method throws an exception,
    00106          * then old connection will be broken.
    00107          *
    00108          * @param socketFilename The filename of the server socket to connect to.
    00109          * @param username The username to use for authenticating with the server.
    00110          * @param userSuppliedPassword The password to use for authenticating with the server.
    00111          * @return this
    00112          * @throws SystemException Something went wrong while connecting to the server.
    00113          * @throws IOException Something went wrong while connecting to the server.
    00114          * @throws RuntimeException Something went wrong.
    00115          * @throws SecurityException Unable to authenticate to the server with the given username and password.
    00116          *                           You may call connect() again with a different username/password.
    00117          * @throws boost::thread_interrupted
    00118          * @post connected()
    00119          */
    00120         MessageClient *connect(const string &socketFilename, const string &username, const StaticString &userSuppliedPassword) {
    00121                 try {
    00122                         fd = connectToUnixServer(socketFilename.c_str());
    00123                         channel = MessageChannel(fd);
    00124                         authenticate(username, userSuppliedPassword);
    00125                         return this;
    00126                 } catch (const RuntimeException &) {
    00127                         silentDisconnect();
    00128                         throw;
    00129                 } catch (const SystemException &) {
    00130                         silentDisconnect();
    00131                         throw;
    00132                 } catch (const IOException &) {
    00133                         silentDisconnect();
    00134                         throw;
    00135                 } catch (const boost::thread_interrupted &) {
    00136                         silentDisconnect();
    00137                         throw;
    00138                 }
    00139         }
    00140
    00141         void disconnect() {
    00142                 if (fd != -1) {
    00143                         fd.close();
    00144                         fd = FileDescriptor();
    00145                         channel = MessageChannel();
    00146                 }
    00147         }
    00148
    00149         bool connected() const {
    00150                 return fd != -1;
    00151         }
    00152
    00153         /**
    00154          * @throws SystemException
    00155          * @throws boost::thread_interrupted
    00156          */
    00157         bool read(vector<string> &args) {
    00158                 try {
    00159                         return channel.read(args);
    00160                 } catch (const SystemException &e) {
    00161                         silentDisconnect();
    00162                         throw;
    00163                 }
    00164         }
    00165
    00166         /**
    00167          * @throws SystemException
    00168          * @throws SecurityException
    00169          * @throws TimeoutException
    00170          * @throws boost::thread_interrupted
    00171          */
    00172         bool readScalar(string &output, unsigned int maxSize = 0, unsigned long long *timeout = NULL) {
    00173                 try {
    00174                         return channel.readScalar(output, maxSize, timeout);
    00175                 } catch (const SystemException &) {
    00176                         silentDisconnect();
    00177                         throw;
    00178                 } catch (const SecurityException &) {
    00179                         silentDisconnect();
    00180                         throw;
    00181                 } catch (const TimeoutException &) {
    00182                         silentDisconnect();
    00183                         throw;
    00184                 }
    00185         }
    00186
    00187         /**
    00188          * @throws SystemExeption
    00189          * @throws IOException
    00190          * @throws boost::thread_interrupted
    00191          */
    00192         int readFileDescriptor(bool negotiate = true) {
    00193                 try {
    00194                         return channel.readFileDescriptor(negotiate);
    00195                 } catch (const SystemException &) {
    00196                         silentDisconnect();
    00197                         throw;
    00198                 } catch (const IOException &) {
    00199                         silentDisconnect();
    00200                         throw;
    00201                 }
    00202         }
    00203
    00204         /**
    00205          * @throws SystemException
    00206          * @throws boost::thread_interrupted
    00207          */
    00208         void write(const char *name, ...) {
    00209                 va_list ap;
    00210                 va_start(ap, name);
    00211                 try {
    00212                         try {
    00213                                 channel.write(name, ap);
    00214                         } catch (const SystemException &) {
    00215                                 silentDisconnect();
    00216                                 throw;
    00217                         }
    00218                         va_end(ap);
    00219                 } catch (...) {
    00220                         va_end(ap);
    00221                         throw;
    00222                 }
    00223         }
    00224
    00225         /**
    00226          * @throws SystemException
    00227          * @throws boost::thread_interrupted
    00228          */
    00229         void writeScalar(const char *data, unsigned int size) {
    00230                 try {
    00231                         channel.writeScalar(data, size);
    00232                 } catch (const SystemException &) {
    00233                         silentDisconnect();
    00234                         throw;
    00235                 }
    00236         }
    00237
    00238         /**
    00239          * @throws SystemException
    00240          * @throws boost::thread_interrupted
    00241          */
    00242         void writeScalar(const StaticString &data) {
    00243                 try {
    00244                         channel.writeScalar(data.c_str(), data.size());
    00245                 } catch (const SystemException &) {
    00246                         silentDisconnect();
    00247                         throw;
    00248                 }
    00249         }
    00250
    00251         /**
    00252          * @throws SystemException
    00253          * @throws boost::thread_interrupted
    00254          */
    00255         void writeFileDescriptor(int fileDescriptor, bool negotiate = true) {
    00256                 try {
    00257                         channel.writeFileDescriptor(fileDescriptor, negotiate);
    00258                 } catch (const SystemException &) {
    00259                         silentDisconnect();
    00260                         throw;
    00261                 }
    00262         }
    00263 };
    00264
    00265 typedef shared_ptr<MessageClient> MessageClientPtr;
    00266
    00267 } // namespace Passenger
    00268
    00269 #endif /* _PASSENGER_MESSAGE_CLIENT_H_ */