passenger 3.0.8 → 3.0.9

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (42) hide show
  1. data/NEWS +9 -0
  2. data/bin/passenger-install-nginx-module +1 -1
  3. data/build/basics.rb +0 -1
  4. data/build/cxx_tests.rb +5 -0
  5. data/build/rpm.rb +1 -1
  6. data/doc/Users guide Apache.html +1 -1
  7. data/doc/Users guide Nginx.html +1 -1
  8. data/ext/apache2/Configuration.cpp +12 -0
  9. data/ext/apache2/Configuration.hpp +12 -1
  10. data/ext/apache2/Hooks.cpp +2 -0
  11. data/ext/common/AgentsStarter.cpp +4 -0
  12. data/ext/common/AgentsStarter.h +2 -0
  13. data/ext/common/AgentsStarter.hpp +4 -0
  14. data/ext/common/Constants.h +1 -1
  15. data/ext/common/Logging.h +481 -261
  16. data/ext/common/LoggingAgent/LoggingServer.h +10 -4
  17. data/ext/common/LoggingAgent/Main.cpp +7 -2
  18. data/ext/common/LoggingAgent/RemoteSender.h +25 -3
  19. data/ext/common/MessageChannel.h +18 -227
  20. data/ext/common/MessageClient.h +95 -92
  21. data/ext/common/Utils/IOUtils.cpp +114 -1
  22. data/ext/common/Utils/IOUtils.h +57 -1
  23. data/ext/common/Utils/MessageIO.h +576 -0
  24. data/ext/nginx/Configuration.c +35 -0
  25. data/ext/nginx/Configuration.h +2 -0
  26. data/ext/nginx/ContentHandler.c +17 -6
  27. data/ext/nginx/ngx_http_passenger_module.c +8 -0
  28. data/lib/phusion_passenger.rb +2 -2
  29. data/lib/phusion_passenger/analytics_logger.rb +174 -117
  30. data/lib/phusion_passenger/app_process.rb +14 -2
  31. data/test/cxx/CxxTestMain.cpp +14 -19
  32. data/test/cxx/IOUtilsTest.cpp +68 -18
  33. data/test/cxx/LoggingTest.cpp +20 -24
  34. data/test/cxx/MessageChannelTest.cpp +1 -1
  35. data/test/cxx/MessageIOTest.cpp +310 -0
  36. data/test/cxx/TestSupport.cpp +47 -0
  37. data/test/cxx/TestSupport.h +8 -0
  38. data/test/ruby/analytics_logger_spec.rb +20 -28
  39. data/test/tut/tut.h +2 -0
  40. metadata +11 -11
  41. data/build/rdoctask.rb +0 -209
  42. data/test/cxx/HttpStatusExtractorTest.cpp +0 -198
@@ -26,12 +26,14 @@
26
26
  #define _PASSENGER_MESSAGE_CLIENT_H_
27
27
 
28
28
  #include <boost/shared_ptr.hpp>
29
+ #include <boost/bind.hpp>
29
30
  #include <string>
30
31
 
31
32
  #include "StaticString.h"
32
- #include "MessageChannel.h"
33
33
  #include "Exceptions.h"
34
+ #include "Utils/MessageIO.h"
34
35
  #include "Utils/IOUtils.h"
36
+ #include "Utils/ScopeGuard.h"
35
37
 
36
38
 
37
39
  namespace Passenger {
@@ -42,17 +44,16 @@ using namespace boost;
42
44
  class MessageClient {
43
45
  protected:
44
46
  FileDescriptor fd;
45
- MessageChannel channel;
46
47
  bool shouldAutoDisconnect;
47
48
 
48
49
  /* sendUsername() and sendPassword() exist and are virtual in order to facilitate unit testing. */
49
50
 
50
- virtual void sendUsername(MessageChannel &channel, const string &username) {
51
- channel.writeScalar(username);
51
+ virtual void sendUsername(int fd, const StaticString &username, unsigned long long *timeout) {
52
+ writeScalarMessage(fd, username);
52
53
  }
53
54
 
54
- virtual void sendPassword(MessageChannel &channel, const StaticString &userSuppliedPassword) {
55
- channel.writeScalar(userSuppliedPassword.c_str(), userSuppliedPassword.size());
55
+ virtual void sendPassword(int fd, const StaticString &userSuppliedPassword, unsigned long long *timeout) {
56
+ writeScalarMessage(fd, userSuppliedPassword);
56
57
  }
57
58
 
58
59
  /**
@@ -64,13 +65,15 @@ protected:
64
65
  * @throws boost::thread_interrupted
65
66
  * @pre <tt>channel</tt> is connected.
66
67
  */
67
- void authenticate(const string &username, const StaticString &userSuppliedPassword) {
68
+ void authenticate(const StaticString &username, const StaticString &userSuppliedPassword,
69
+ unsigned long long *timeout)
70
+ {
68
71
  vector<string> args;
69
72
 
70
- sendUsername(channel, username);
71
- sendPassword(channel, userSuppliedPassword);
73
+ sendUsername(fd, username, timeout);
74
+ sendPassword(fd, userSuppliedPassword, timeout);
72
75
 
73
- if (!channel.read(args)) {
76
+ if (!readArrayMessage(fd, args, timeout)) {
74
77
  throw IOException("The message server did not send an authentication response.");
75
78
  } else if (args.size() != 1) {
76
79
  throw IOException("The authentication response that the message server sent is not valid.");
@@ -79,11 +82,15 @@ protected:
79
82
  }
80
83
  }
81
84
 
85
+ void checkConnection() {
86
+ if (!connected()) {
87
+ throw IOException("Not connected");
88
+ }
89
+ }
90
+
82
91
  void autoDisconnect() {
83
92
  if (shouldAutoDisconnect) {
84
- // Closes the connection without throwing close exceptions.
85
- fd = FileDescriptor();
86
- channel = MessageChannel();
93
+ fd.close(false);
87
94
  }
88
95
  }
89
96
 
@@ -122,48 +129,35 @@ public:
122
129
  * @throws boost::thread_interrupted
123
130
  * @post connected()
124
131
  */
125
- MessageClient *connect(const string &serverAddress, const string &username,
132
+ MessageClient *connect(const string &serverAddress, const StaticString &username,
126
133
  const StaticString &userSuppliedPassword)
127
134
  {
128
135
  TRACE_POINT();
129
- try {
130
- fd = connectToServer(serverAddress.c_str());
131
- channel = MessageChannel(fd);
132
-
133
- vector<string> args;
134
- if (!read(args)) {
135
- throw IOException("The message server closed the connection before sending a version identifier.");
136
- }
137
- if (args.size() != 2 || args[0] != "version") {
138
- throw IOException("The message server didn't sent a valid version identifier.");
139
- }
140
- if (args[1] != "1") {
141
- string message = string("Unsupported message server protocol version ") +
142
- args[1] + ".";
143
- throw IOException(message);
144
- }
145
-
146
- authenticate(username, userSuppliedPassword);
147
- return this;
148
- } catch (const RuntimeException &) {
149
- autoDisconnect();
150
- throw;
151
- } catch (const SystemException &) {
152
- autoDisconnect();
153
- throw;
154
- } catch (const IOException &) {
155
- autoDisconnect();
156
- throw;
157
- } catch (const boost::thread_interrupted &) {
158
- autoDisconnect();
159
- throw;
136
+ ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
137
+
138
+ fd = connectToServer(serverAddress.c_str());
139
+
140
+ vector<string> args;
141
+ if (!readArrayMessage(fd, args)) {
142
+ throw IOException("The message server closed the connection before sending a version identifier.");
160
143
  }
144
+ if (args.size() != 2 || args[0] != "version") {
145
+ throw IOException("The message server didn't sent a valid version identifier.");
146
+ }
147
+ if (args[1] != "1") {
148
+ string message = string("Unsupported message server protocol version ") +
149
+ args[1] + ".";
150
+ throw IOException(message);
151
+ }
152
+
153
+ authenticate(username, userSuppliedPassword, NULL);
154
+
155
+ g.clear();
156
+ return this;
161
157
  }
162
158
 
163
159
  void disconnect() {
164
160
  fd.close();
165
- fd = FileDescriptor();
166
- channel = MessageChannel();
167
161
  }
168
162
 
169
163
  bool connected() const {
@@ -180,15 +174,24 @@ public:
180
174
 
181
175
  /**
182
176
  * @throws SystemException
177
+ * @throws TimeoutException
183
178
  * @throws boost::thread_interrupted
184
179
  */
185
- bool read(vector<string> &args) {
186
- try {
187
- return channel.read(args);
188
- } catch (const SystemException &e) {
189
- autoDisconnect();
190
- throw;
191
- }
180
+ bool read(vector<string> &args, unsigned long long *timeout = NULL) {
181
+ return readArray(args);
182
+ }
183
+
184
+ /**
185
+ * @throws SystemException
186
+ * @throws TimeoutException
187
+ * @throws boost::thread_interrupted
188
+ */
189
+ bool readArray(vector<string> &args, unsigned long long *timeout = NULL) {
190
+ checkConnection();
191
+ ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
192
+ bool result = readArrayMessage(fd, args, timeout);
193
+ g.clear();
194
+ return result;
192
195
  }
193
196
 
194
197
  /**
@@ -198,17 +201,15 @@ public:
198
201
  * @throws boost::thread_interrupted
199
202
  */
200
203
  bool readScalar(string &output, unsigned int maxSize = 0, unsigned long long *timeout = NULL) {
204
+ checkConnection();
205
+ ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
201
206
  try {
202
- return channel.readScalar(output, maxSize, timeout);
203
- } catch (const SystemException &) {
204
- autoDisconnect();
205
- throw;
206
- } catch (const SecurityException &) {
207
- autoDisconnect();
208
- throw;
209
- } catch (const TimeoutException &) {
210
- autoDisconnect();
211
- throw;
207
+ output = readScalarMessage(fd, maxSize, timeout);
208
+ g.clear();
209
+ return true;
210
+ } catch (const EOFException &) {
211
+ g.clear();
212
+ return false;
212
213
  }
213
214
  }
214
215
 
@@ -218,15 +219,16 @@ public:
218
219
  * @throws boost::thread_interrupted
219
220
  */
220
221
  int readFileDescriptor(bool negotiate = true) {
221
- try {
222
- return channel.readFileDescriptor(negotiate);
223
- } catch (const SystemException &) {
224
- autoDisconnect();
225
- throw;
226
- } catch (const IOException &) {
227
- autoDisconnect();
228
- throw;
222
+ checkConnection();
223
+ ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
224
+ int result;
225
+ if (negotiate) {
226
+ result = Passenger::readFileDescriptorWithNegotiation(fd);
227
+ } else {
228
+ result = Passenger::readFileDescriptor(fd);
229
229
  }
230
+ g.clear();
231
+ return result;
230
232
  }
231
233
 
232
234
  /**
@@ -234,11 +236,12 @@ public:
234
236
  * @throws boost::thread_interrupted
235
237
  */
236
238
  void write(const char *name, ...) {
239
+ checkConnection();
237
240
  va_list ap;
238
241
  va_start(ap, name);
239
242
  try {
240
243
  try {
241
- channel.write(name, ap);
244
+ writeArrayMessage(fd, name, ap);
242
245
  } catch (const SystemException &) {
243
246
  autoDisconnect();
244
247
  throw;
@@ -252,28 +255,26 @@ public:
252
255
 
253
256
  /**
254
257
  * @throws SystemException
258
+ * @throws TimeoutException
255
259
  * @throws boost::thread_interrupted
256
260
  */
257
- void writeScalar(const char *data, unsigned int size) {
258
- try {
259
- channel.writeScalar(data, size);
260
- } catch (const SystemException &) {
261
- autoDisconnect();
262
- throw;
263
- }
261
+ void writeScalar(const char *data, unsigned int size, unsigned long long *timeout = NULL) {
262
+ checkConnection();
263
+ ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
264
+ writeScalarMessage(fd, data, size, timeout);
265
+ g.clear();
264
266
  }
265
267
 
266
268
  /**
267
269
  * @throws SystemException
270
+ * @throws TimeoutException
268
271
  * @throws boost::thread_interrupted
269
272
  */
270
- void writeScalar(const StaticString &data) {
271
- try {
272
- channel.writeScalar(data.c_str(), data.size());
273
- } catch (const SystemException &) {
274
- autoDisconnect();
275
- throw;
276
- }
273
+ void writeScalar(const StaticString &data, unsigned long long *timeout = NULL) {
274
+ checkConnection();
275
+ ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
276
+ writeScalarMessage(fd, data, timeout);
277
+ g.clear();
277
278
  }
278
279
 
279
280
  /**
@@ -281,12 +282,14 @@ public:
281
282
  * @throws boost::thread_interrupted
282
283
  */
283
284
  void writeFileDescriptor(int fileDescriptor, bool negotiate = true) {
284
- try {
285
- channel.writeFileDescriptor(fileDescriptor, negotiate);
286
- } catch (const SystemException &) {
287
- autoDisconnect();
288
- throw;
285
+ checkConnection();
286
+ ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this));
287
+ if (negotiate) {
288
+ Passenger::writeFileDescriptorWithNegotiation(fd, fileDescriptor);
289
+ } else {
290
+ Passenger::writeFileDescriptor(fd, fileDescriptor);
289
291
  }
292
+ g.clear();
290
293
  }
291
294
  };
292
295
 
@@ -860,7 +860,7 @@ eraseBeginningOfIoVec(struct iovec *iov, size_t count, size_t index, size_t offs
860
860
  }
861
861
 
862
862
  void
863
- gatheredWrite(int fd, const StaticString data[], unsigned int count) {
863
+ gatheredWrite(int fd, const StaticString data[], unsigned int count, unsigned long long *timeout) {
864
864
  struct iovec iov[count];
865
865
  size_t total, iovCount;
866
866
  size_t written = 0;
@@ -868,6 +868,9 @@ gatheredWrite(int fd, const StaticString data[], unsigned int count) {
868
868
  total = staticStringArrayToIoVec(data, count, iov, iovCount);
869
869
 
870
870
  while (written < total) {
871
+ if (timeout != NULL && !waitUntilWritable(fd, timeout)) {
872
+ throw TimeoutException("Cannot write enough data within the specified timeout");
873
+ }
871
874
  ssize_t ret = writevFunction(fd, iov, std::min(iovCount, (size_t) IOV_MAX));
872
875
  if (ret == -1) {
873
876
  int e = errno;
@@ -892,6 +895,116 @@ setWritevFunction(WritevFunction func) {
892
895
  }
893
896
  }
894
897
 
898
+ int
899
+ readFileDescriptor(int fd, unsigned long long *timeout) {
900
+ if (timeout != NULL && !waitUntilReadable(fd, timeout)) {
901
+ throw TimeoutException("Cannot receive file descriptor within the specified timeout");
902
+ }
903
+
904
+ struct msghdr msg;
905
+ struct iovec vec;
906
+ char dummy[1];
907
+ #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
908
+ // File descriptor passing macros (CMSG_*) seem to be broken
909
+ // on 64-bit MacOS X. This structure works around the problem.
910
+ struct {
911
+ struct cmsghdr header;
912
+ int fd;
913
+ } control_data;
914
+ #define EXPECTED_CMSG_LEN sizeof(control_data)
915
+ #else
916
+ char control_data[CMSG_SPACE(sizeof(int))];
917
+ #define EXPECTED_CMSG_LEN CMSG_LEN(sizeof(int))
918
+ #endif
919
+ struct cmsghdr *control_header;
920
+ int ret;
921
+
922
+ msg.msg_name = NULL;
923
+ msg.msg_namelen = 0;
924
+
925
+ dummy[0] = '\0';
926
+ vec.iov_base = dummy;
927
+ vec.iov_len = sizeof(dummy);
928
+ msg.msg_iov = &vec;
929
+ msg.msg_iovlen = 1;
930
+
931
+ msg.msg_control = (caddr_t) &control_data;
932
+ msg.msg_controllen = sizeof(control_data);
933
+ msg.msg_flags = 0;
934
+
935
+ ret = syscalls::recvmsg(fd, &msg, 0);
936
+ if (ret == -1) {
937
+ throw SystemException("Cannot read file descriptor with recvmsg()", errno);
938
+ }
939
+
940
+ control_header = CMSG_FIRSTHDR(&msg);
941
+ if (control_header == NULL) {
942
+ throw IOException("No valid file descriptor received.");
943
+ }
944
+ if (control_header->cmsg_len != EXPECTED_CMSG_LEN
945
+ || control_header->cmsg_level != SOL_SOCKET
946
+ || control_header->cmsg_type != SCM_RIGHTS) {
947
+ throw IOException("No valid file descriptor received.");
948
+ }
949
+
950
+ #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
951
+ return control_data.fd;
952
+ #else
953
+ return *((int *) CMSG_DATA(control_header));
954
+ #endif
955
+ }
956
+
957
+ void
958
+ writeFileDescriptor(int fd, int fdToSend, unsigned long long *timeout) {
959
+ if (timeout != NULL && !waitUntilWritable(fd, timeout)) {
960
+ throw TimeoutException("Cannot send file descriptor within the specified timeout");
961
+ }
962
+
963
+ struct msghdr msg;
964
+ struct iovec vec;
965
+ char dummy[1];
966
+ #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
967
+ struct {
968
+ struct cmsghdr header;
969
+ int fd;
970
+ } control_data;
971
+ #else
972
+ char control_data[CMSG_SPACE(sizeof(int))];
973
+ #endif
974
+ struct cmsghdr *control_header;
975
+ int ret;
976
+
977
+ msg.msg_name = NULL;
978
+ msg.msg_namelen = 0;
979
+
980
+ /* Linux and Solaris require msg_iov to be non-NULL. */
981
+ dummy[0] = '\0';
982
+ vec.iov_base = dummy;
983
+ vec.iov_len = sizeof(dummy);
984
+ msg.msg_iov = &vec;
985
+ msg.msg_iovlen = 1;
986
+
987
+ msg.msg_control = (caddr_t) &control_data;
988
+ msg.msg_controllen = sizeof(control_data);
989
+ msg.msg_flags = 0;
990
+
991
+ control_header = CMSG_FIRSTHDR(&msg);
992
+ control_header->cmsg_level = SOL_SOCKET;
993
+ control_header->cmsg_type = SCM_RIGHTS;
994
+ #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
995
+ control_header->cmsg_len = sizeof(control_data);
996
+ control_data.fd = fdToSend;
997
+ #else
998
+ control_header->cmsg_len = CMSG_LEN(sizeof(int));
999
+ memcpy(CMSG_DATA(control_header), &fdToSend, sizeof(int));
1000
+ #endif
1001
+
1002
+ ret = syscalls::sendmsg(fd, &msg, 0);
1003
+ if (ret == -1) {
1004
+ throw SystemException("Cannot send file descriptor with sendmsg()", errno);
1005
+ }
1006
+ }
1007
+
895
1008
  void
896
1009
  safelyClose(int fd) {
897
1010
  if (syscalls::close(fd) == -1) {
@@ -345,10 +345,19 @@ ssize_t gatheredWrite(int fd, const StaticString data[], unsigned int dataCount,
345
345
  * @param fd The file descriptor to write to.
346
346
  * @param data An array of buffers to be written.
347
347
  * @param count Number of items in <em>data</em>.
348
+ * @param timeout A pointer to an integer, which specifies the maximum number of
349
+ * microseconds that may be spent on writing all the data.
350
+ * If the timeout expired then TimeoutException will be thrown.
351
+ * If this function returns without throwing an exception, then the
352
+ * total number of microseconds spent on writing will be deducted
353
+ * from <tt>timeout</tt>.
354
+ * Pass NULL if you do not want to enforce a timeout.
348
355
  * @throws SystemException Something went wrong.
356
+ * @throws TimeoutException Unable to write all given data within
357
+ * <tt>timeout</tt> microseconds.
349
358
  * @throws boost::thread_interrupted
350
359
  */
351
- void gatheredWrite(int fd, const StaticString data[], unsigned int dataCount);
360
+ void gatheredWrite(int fd, const StaticString data[], unsigned int dataCount, unsigned long long *timeout = NULL);
352
361
 
353
362
  /**
354
363
  * Sets a writev-emulating function that gatheredWrite() should call instead of the real writev().
@@ -356,6 +365,53 @@ void gatheredWrite(int fd, const StaticString data[], unsigned int dataCount)
356
365
  */
357
366
  void setWritevFunction(WritevFunction func);
358
367
 
368
+ /**
369
+ * Receive a file descriptor over the given Unix domain socket.
370
+ * This is a low-level function that directly wraps the Unix file
371
+ * descriptor passing system calls. You should not use this directly;
372
+ * instead you should use readFileDescriptorWithNegotiation() from MessageIO.h
373
+ * which is safer. See MessageIO.h for more information about the
374
+ * negotiation protocol for file descriptor passing.
375
+ *
376
+ * @param timeout A pointer to an integer, which specifies the maximum number of
377
+ * microseconds that may be spent on receiving the file descriptor.
378
+ * If the timeout expired then TimeoutException will be thrown.
379
+ * If this function returns without throwing an exception, then the
380
+ * total number of microseconds spent on receiving will be deducted
381
+ * from <tt>timeout</tt>.
382
+ * Pass NULL if you do not want to enforce a timeout.
383
+ * @return The received file descriptor.
384
+ * @throws SystemException Something went wrong.
385
+ * @throws IOException Whatever was received doesn't seem to be a
386
+ * file descriptor.
387
+ * @throws TimeoutException Unable to receive a file descriptor within
388
+ * <tt>timeout</tt> microseconds.
389
+ * @throws boost::thread_interrupted
390
+ */
391
+ int readFileDescriptor(int fd, unsigned long long *timeout = NULL);
392
+
393
+ /**
394
+ * Pass the file descriptor 'fdToSend' over the Unix socket 'fd'.
395
+ * This is a low-level function that directly wraps the Unix file
396
+ * descriptor passing system calls. You should not use this directly;
397
+ * instead you should use writeFileDescriptorWithNegotiation() from MessageIO.h
398
+ * which is safer. See MessageIO.h for more information about the
399
+ * negotiation protocol for file descriptor passing.
400
+ *
401
+ * @param timeout A pointer to an integer, which specifies the maximum number of
402
+ * microseconds that may be spent on trying to pass the file descriptor.
403
+ * If the timeout expired then TimeoutException will be thrown.
404
+ * If this function returns without throwing an exception, then the
405
+ * total number of microseconds spent on writing will be deducted
406
+ * from <tt>timeout</tt>.
407
+ * Pass NULL if you do not want to enforce a timeout.
408
+ * @throws SystemException Something went wrong.
409
+ * @throws TimeoutException Unable to pass the file descriptor within
410
+ * <tt>timeout</tt> microseconds.
411
+ * @throws boost::thread_interrupted
412
+ */
413
+ void writeFileDescriptor(int fd, int fdToSend, unsigned long long *timeout = NULL);
414
+
359
415
  /**
360
416
  * Closes the given file descriptor and throws an exception if anything goes wrong.
361
417
  * This function also works around certain close() bugs on certain operating systems.