PageRenderTime 40ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/ext/common/MessageClient.h

http://github.com/FooBarWidget/passenger
C Header | 300 lines | 163 code | 36 blank | 101 comment | 21 complexity | f5625c67dd3ab42ec05ae9cd284aa02b MD5 | raw file
Possible License(s): BSD-3-Clause, BSD-2-Clause, ISC
  1. /*
  2. * Phusion Passenger - https://www.phusionpassenger.com/
  3. * Copyright (c) 2010-2015 Phusion
  4. *
  5. * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. * THE SOFTWARE.
  24. */
  25. #ifndef _PASSENGER_MESSAGE_CLIENT_H_
  26. #define _PASSENGER_MESSAGE_CLIENT_H_
  27. #include <boost/shared_ptr.hpp>
  28. #include <boost/bind.hpp>
  29. #include <string>
  30. #include <StaticString.h>
  31. #include <Exceptions.h>
  32. #include <Utils/MessageIO.h>
  33. #include <Utils/IOUtils.h>
  34. #include <Utils/ScopeGuard.h>
  35. namespace Passenger {
  36. using namespace std;
  37. using namespace boost;
  38. class MessageClient {
  39. protected:
  40. FileDescriptor fd;
  41. bool shouldAutoDisconnect;
  42. /* sendUsername() and sendPassword() exist and are virtual in order to facilitate unit testing. */
  43. virtual void sendUsername(int fd, const StaticString &username, unsigned long long *timeout) {
  44. writeScalarMessage(fd, username);
  45. }
  46. virtual void sendPassword(int fd, const StaticString &userSuppliedPassword, unsigned long long *timeout) {
  47. writeScalarMessage(fd, userSuppliedPassword);
  48. }
  49. /**
  50. * Authenticate to the server with the given username and password.
  51. *
  52. * @throws SystemException An error occurred while reading data from or sending data to the server.
  53. * @throws IOException An error occurred while reading data from or sending data to the server.
  54. * @throws SecurityException The server denied authentication.
  55. * @throws boost::thread_interrupted
  56. * @pre <tt>channel</tt> is connected.
  57. */
  58. void authenticate(const StaticString &username, const StaticString &userSuppliedPassword,
  59. unsigned long long *timeout)
  60. {
  61. vector<string> args;
  62. sendUsername(fd, username, timeout);
  63. sendPassword(fd, userSuppliedPassword, timeout);
  64. if (!readArrayMessage(fd, args, timeout)) {
  65. throw IOException("The message server did not send an authentication response.");
  66. } else if (args.size() != 1) {
  67. throw IOException("The authentication response that the message server sent is not valid.");
  68. } else if (args[0] != "ok") {
  69. throw SecurityException("The message server denied authentication: " + args[0]);
  70. }
  71. }
  72. void checkConnection() {
  73. if (!connected()) {
  74. throw IOException("Not connected");
  75. }
  76. }
  77. void autoDisconnect() {
  78. if (shouldAutoDisconnect) {
  79. fd.close(false);
  80. }
  81. }
  82. public:
  83. /**
  84. * Create a new MessageClient object. It doesn't actually connect to the server until
  85. * you call connect().
  86. */
  87. MessageClient() {
  88. /* The reason why we don't connect right away is because we want to make
  89. * certain methods virtual for unit testing purposes. We can't call
  90. * virtual methods in the constructor. :-(
  91. */
  92. shouldAutoDisconnect = true;
  93. }
  94. virtual ~MessageClient() { }
  95. /**
  96. * Connect to the given MessageServer. If a connection was already established,
  97. * then the old connection will be closed and a new connection will be established.
  98. *
  99. * If this MessageClient was in a connected state, and this method throws an exception,
  100. * then old connection will be broken.
  101. *
  102. * @param serverAddress The address of the server to connect to, in the format
  103. * as specified by getSocketAddressType().
  104. * @param username The username to use for authenticating with the server.
  105. * @param userSuppliedPassword The password to use for authenticating with the server.
  106. * @return this
  107. * @throws SystemException Something went wrong while connecting to the server.
  108. * @throws IOException Something went wrong while connecting to the server.
  109. * @throws RuntimeException Something went wrong.
  110. * @throws SecurityException Unable to authenticate to the server with the given username and password.
  111. * You may call connect() again with a different username/password.
  112. * @throws boost::thread_interrupted
  113. * @post connected()
  114. */
  115. MessageClient *connect(const string &serverAddress, const StaticString &username,
  116. const StaticString &userSuppliedPassword)
  117. {
  118. TRACE_POINT();
  119. ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
  120. fd.assign(connectToServer(serverAddress.c_str(), __FILE__, __LINE__), NULL, 0);
  121. vector<string> args;
  122. if (!readArrayMessage(fd, args)) {
  123. throw IOException("The message server closed the connection before sending a version identifier.");
  124. }
  125. if (args.size() != 2 || args[0] != "version") {
  126. throw IOException("The message server didn't sent a valid version identifier.");
  127. }
  128. if (args[1] != "1") {
  129. string message = string("Unsupported message server protocol version ") +
  130. args[1] + ".";
  131. throw IOException(message);
  132. }
  133. authenticate(username, userSuppliedPassword, NULL);
  134. g.clear();
  135. return this;
  136. }
  137. void disconnect() {
  138. fd.close();
  139. }
  140. bool connected() const {
  141. return fd != -1;
  142. }
  143. void setAutoDisconnect(bool value) {
  144. shouldAutoDisconnect = value;
  145. }
  146. FileDescriptor getConnection() const {
  147. return fd;
  148. }
  149. /**
  150. * @throws SystemException
  151. * @throws TimeoutException
  152. * @throws boost::thread_interrupted
  153. */
  154. bool read(vector<string> &args, unsigned long long *timeout = NULL) {
  155. return readArray(args);
  156. }
  157. /**
  158. * @throws SystemException
  159. * @throws TimeoutException
  160. * @throws boost::thread_interrupted
  161. */
  162. bool readArray(vector<string> &args, unsigned long long *timeout = NULL) {
  163. checkConnection();
  164. ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
  165. bool result = readArrayMessage(fd, args, timeout);
  166. g.clear();
  167. return result;
  168. }
  169. /**
  170. * @throws SystemException
  171. * @throws SecurityException
  172. * @throws TimeoutException
  173. * @throws boost::thread_interrupted
  174. */
  175. bool readScalar(string &output, unsigned int maxSize = 0, unsigned long long *timeout = NULL) {
  176. checkConnection();
  177. ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
  178. try {
  179. output = readScalarMessage(fd, maxSize, timeout);
  180. g.clear();
  181. return true;
  182. } catch (const EOFException &) {
  183. g.clear();
  184. return false;
  185. }
  186. }
  187. /**
  188. * @throws SystemExeption
  189. * @throws IOException
  190. * @throws boost::thread_interrupted
  191. */
  192. int readFileDescriptor(bool negotiate = true) {
  193. checkConnection();
  194. ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
  195. int result;
  196. if (negotiate) {
  197. result = Passenger::readFileDescriptorWithNegotiation(fd);
  198. } else {
  199. result = Passenger::readFileDescriptor(fd);
  200. }
  201. g.clear();
  202. return result;
  203. }
  204. /**
  205. * @throws SystemException
  206. * @throws boost::thread_interrupted
  207. */
  208. void write(const char *name, ...) {
  209. checkConnection();
  210. va_list ap;
  211. va_start(ap, name);
  212. try {
  213. try {
  214. writeArrayMessageVA(fd, name, ap);
  215. } catch (const SystemException &) {
  216. autoDisconnect();
  217. throw;
  218. }
  219. va_end(ap);
  220. } catch (...) {
  221. va_end(ap);
  222. throw;
  223. }
  224. }
  225. /**
  226. * @throws SystemException
  227. * @throws TimeoutException
  228. * @throws boost::thread_interrupted
  229. */
  230. void writeScalar(const char *data, unsigned int size, unsigned long long *timeout = NULL) {
  231. checkConnection();
  232. ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
  233. writeScalarMessage(fd, data, size, timeout);
  234. g.clear();
  235. }
  236. /**
  237. * @throws SystemException
  238. * @throws TimeoutException
  239. * @throws boost::thread_interrupted
  240. */
  241. void writeScalar(const StaticString &data, unsigned long long *timeout = NULL) {
  242. checkConnection();
  243. ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
  244. writeScalarMessage(fd, data, timeout);
  245. g.clear();
  246. }
  247. /**
  248. * @throws SystemException
  249. * @throws boost::thread_interrupted
  250. */
  251. void writeFileDescriptor(int fileDescriptor, bool negotiate = true) {
  252. checkConnection();
  253. ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
  254. if (negotiate) {
  255. Passenger::writeFileDescriptorWithNegotiation(fd, fileDescriptor);
  256. } else {
  257. Passenger::writeFileDescriptor(fd, fileDescriptor);
  258. }
  259. g.clear();
  260. }
  261. };
  262. typedef boost::shared_ptr<MessageClient> MessageClientPtr;
  263. } // namespace Passenger
  264. #endif /* _PASSENGER_MESSAGE_CLIENT_H_ */