passenger 3.0.9 → 3.0.10

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 (67) hide show
  1. data/NEWS +32 -0
  2. data/Rakefile +1 -1
  3. data/build/common_library.rb +6 -1
  4. data/build/config.rb +3 -1
  5. data/doc/Users guide Apache.html +120 -39
  6. data/doc/Users guide Apache.txt +64 -0
  7. data/doc/Users guide Nginx.html +50 -2
  8. data/doc/Users guide Nginx.txt +29 -0
  9. data/ext/apache2/Bucket.cpp +7 -5
  10. data/ext/apache2/Bucket.h +2 -1
  11. data/ext/apache2/Configuration.cpp +8 -0
  12. data/ext/apache2/Configuration.hpp +9 -5
  13. data/ext/apache2/HelperAgent.cpp +4 -5
  14. data/ext/apache2/Hooks.cpp +1 -6
  15. data/ext/boost/thread/exceptions.hpp +7 -1
  16. data/ext/boost/thread/locks.hpp +11 -11
  17. data/ext/common/AgentBase.cpp +21 -1
  18. data/ext/common/AgentsStarter.hpp +22 -21
  19. data/ext/common/ApplicationPool/Client.h +0 -8
  20. data/ext/common/ApplicationPool/Server.h +22 -21
  21. data/ext/common/Constants.h +1 -1
  22. data/ext/common/EventedMessageServer.h +6 -2
  23. data/ext/common/IniFile.h +4 -4
  24. data/ext/common/Logging.h +1 -1
  25. data/ext/common/LoggingAgent/LoggingServer.h +2 -2
  26. data/ext/common/LoggingAgent/Main.cpp +2 -2
  27. data/ext/common/MessageChannel.h +20 -62
  28. data/ext/common/MessageReadersWriters.h +4 -4
  29. data/ext/common/MessageServer.h +18 -18
  30. data/ext/common/Process.h +4 -5
  31. data/ext/common/Session.h +6 -40
  32. data/ext/common/SpawnManager.h +20 -25
  33. data/ext/common/Utils.cpp +1 -1
  34. data/ext/common/Utils/Dechunker.h +1 -1
  35. data/ext/common/Utils/MessageIO.h +109 -14
  36. data/ext/common/Utils/StreamBoyerMooreHorspool.h +20 -14
  37. data/ext/common/Utils/VariantMap.h +9 -27
  38. data/ext/common/Watchdog.cpp +53 -42
  39. data/ext/libev/config.h +122 -0
  40. data/ext/nginx/Configuration.c +62 -0
  41. data/ext/nginx/Configuration.h +5 -0
  42. data/ext/nginx/ContentHandler.c +46 -19
  43. data/ext/nginx/HelperAgent.cpp +6 -5
  44. data/ext/nginx/config +12 -12
  45. data/ext/oxt/system_calls.cpp +10 -1
  46. data/ext/ruby/extconf.rb +0 -1
  47. data/ext/ruby/passenger_native_support.c +2 -2
  48. data/helper-scripts/prespawn +1 -1
  49. data/lib/phusion_passenger.rb +4 -4
  50. data/lib/phusion_passenger/classic_rails/application_spawner.rb +2 -2
  51. data/lib/phusion_passenger/dependencies.rb +6 -1
  52. data/lib/phusion_passenger/platform_info.rb +9 -0
  53. data/lib/phusion_passenger/platform_info/compiler.rb +5 -0
  54. data/lib/phusion_passenger/platform_info/operating_system.rb +1 -1
  55. data/lib/phusion_passenger/platform_info/ruby.rb +5 -5
  56. data/lib/phusion_passenger/rack/application_spawner.rb +6 -3
  57. data/lib/phusion_passenger/utils.rb +2 -2
  58. data/lib/phusion_passenger/wsgi/application_spawner.rb +1 -1
  59. data/resources/mime.types +2 -0
  60. data/test/cxx/LoggingTest.cpp +10 -12
  61. data/test/cxx/MessageIOTest.cpp +53 -3
  62. data/test/cxx/MessageReadersWritersTest.cpp +5 -2
  63. data/test/cxx/MessageServerTest.cpp +3 -1
  64. data/test/integration_tests/nginx_tests.rb +14 -1
  65. data/test/stub/rack/config.ru +2 -0
  66. data/test/tut/tut.h +9 -3
  67. metadata +5 -4
@@ -33,13 +33,13 @@
33
33
  #include <sys/types.h>
34
34
  #include <cstring>
35
35
  #include <arpa/inet.h>
36
- #include "StaticString.h"
37
- #include "Exceptions.h"
38
- #include "Utils/MemZeroGuard.h"
36
+ #include <StaticString.h>
37
+ #include <Exceptions.h>
38
+ #include <Utils/MemZeroGuard.h>
39
39
 
40
40
  /**
41
41
  * This file provides a bunch of classes for reading and writing messages in the
42
- * MessageChannel format. Unlike MessageChannel, whose operations take control over
42
+ * MessageIO format. Unlike MessageIO functions, whose operations take control over
43
43
  * the I/O handle and may block, these classes act like parsers and data generators.
44
44
  * To read messages one must feed data to them. To write messages one must instruct
45
45
  * the classes to generate a bunch of data. These classes will never block, making
@@ -70,7 +70,7 @@ using namespace oxt;
70
70
  * - Designed for simple request/response cycles. That is, a client sends a request, and
71
71
  * the server may respond with arbitrary data. The server does not respond sporadically,
72
72
  * i.e. it only responds after a request.
73
- * - Requests are discrete MessageChannel array messages, not byte streams.
73
+ * - Requests are MessageIO array messages.
74
74
  * - Connections are authenticated. Connecting clients must send a username and password,
75
75
  * which are then checked against an accounts database. The associated account is known
76
76
  * throughout the entire connection life time so that it's possible to implement
@@ -136,7 +136,7 @@ using namespace oxt;
136
136
  * {
137
137
  * if (args[0] == "ping") {
138
138
  * MyContext *myContext = (MyContext *) specificContext.get();
139
- * commonContext.channel.write("pong", toString(specificContext->count).c_str(), NULL);
139
+ * writeArrayMessage(commonContext.fd, "pong", toString(specificContext->count).c_str(), NULL);
140
140
  * specificContext->count++;
141
141
  * return true;
142
142
  * } else {
@@ -195,7 +195,7 @@ public:
195
195
 
196
196
  /** Returns a string representation for this client context. */
197
197
  string name() {
198
- return toString(channel.filenum());
198
+ return toString(fd);
199
199
  }
200
200
 
201
201
  /**
@@ -210,10 +210,10 @@ public:
210
210
  void requireRights(Account::Rights rights) {
211
211
  if (!account->hasRights(rights)) {
212
212
  P_TRACE(2, "Security error: insufficient rights to execute this command.");
213
- channel.write("SecurityException", "Insufficient rights to execute this command.", NULL);
213
+ writeArrayMessage(fd, "SecurityException", "Insufficient rights to execute this command.", NULL);
214
214
  throw SecurityException("Insufficient rights to execute this command.");
215
215
  } else {
216
- channel.write("Passed security", NULL);
216
+ writeArrayMessage(fd, "Passed security", NULL);
217
217
  }
218
218
  }
219
219
  };
@@ -356,45 +356,45 @@ protected:
356
356
  *
357
357
  * @return A smart pointer to an Account object, or NULL if authentication failed.
358
358
  */
359
- AccountPtr authenticate(FileDescriptor &client) {
360
- MessageChannel channel(client);
359
+ AccountPtr authenticate(const FileDescriptor &client) {
361
360
  string username, password;
362
361
  MemZeroGuard passwordGuard(password);
363
362
  unsigned long long timeout = loginTimeout;
364
363
 
365
364
  try {
366
- channel.write("version", "1", NULL);
365
+ writeArrayMessage(client, &timeout, "version", "1", NULL);
367
366
 
368
367
  try {
369
- if (!channel.readScalar(username, MESSAGE_SERVER_MAX_USERNAME_SIZE, &timeout)) {
368
+ if (!readScalarMessage(client, username, MESSAGE_SERVER_MAX_USERNAME_SIZE, &timeout)) {
370
369
  return AccountPtr();
371
370
  }
372
371
  } catch (const SecurityException &) {
373
- channel.write("The supplied username is too long.", NULL);
372
+ writeArrayMessage(client, &timeout, "The supplied username is too long.", NULL);
374
373
  return AccountPtr();
375
374
  }
376
375
 
377
376
  try {
378
- if (!channel.readScalar(password, MESSAGE_SERVER_MAX_PASSWORD_SIZE, &timeout)) {
377
+ if (!readScalarMessage(client, password, MESSAGE_SERVER_MAX_PASSWORD_SIZE, &timeout)) {
379
378
  return AccountPtr();
380
379
  }
381
380
  } catch (const SecurityException &) {
382
- channel.write("The supplied password is too long.", NULL);
381
+ writeArrayMessage(client, &timeout, "The supplied password is too long.", NULL);
383
382
  return AccountPtr();
384
383
  }
385
384
 
386
385
  AccountPtr account = accountsDatabase->authenticate(username, password);
387
386
  passwordGuard.zeroNow();
388
387
  if (account == NULL) {
389
- channel.write("Invalid username or password.", NULL);
388
+ writeArrayMessage(client, &timeout, "Invalid username or password.", NULL);
390
389
  return AccountPtr();
391
390
  } else {
392
- channel.write("ok", NULL);
391
+ writeArrayMessage(client, &timeout, "ok", NULL);
393
392
  return account;
394
393
  }
395
394
  } catch (const SystemException &) {
396
395
  return AccountPtr();
397
396
  } catch (const TimeoutException &) {
397
+ P_WARN("Login timeout");
398
398
  return AccountPtr();
399
399
  }
400
400
  }
@@ -439,7 +439,7 @@ protected:
439
439
  /**
440
440
  * The main function for a thread which handles a client.
441
441
  */
442
- void clientHandlingMainLoop(FileDescriptor &client) {
442
+ void clientHandlingMainLoop(FileDescriptor client) {
443
443
  TRACE_POINT();
444
444
  vector<string> args;
445
445
 
@@ -459,7 +459,7 @@ protected:
459
459
 
460
460
  while (!this_thread::interruption_requested()) {
461
461
  UPDATE_TRACE_POINT();
462
- if (!commonContext.channel.read(args)) {
462
+ if (!readArrayMessage(commonContext.fd, args)) {
463
463
  // Client closed connection.
464
464
  break;
465
465
  }
@@ -503,7 +503,7 @@ public:
503
503
  MessageServer(const string &socketFilename, AccountsDatabasePtr accountsDatabase) {
504
504
  this->socketFilename = socketFilename;
505
505
  this->accountsDatabase = accountsDatabase;
506
- loginTimeout = 2000;
506
+ loginTimeout = 2000000;
507
507
  startListening();
508
508
  }
509
509
 
@@ -562,7 +562,7 @@ public:
562
562
  }
563
563
 
564
564
  /**
565
- * Sets the maximum number of milliseconds that clients may spend on logging in.
565
+ * Sets the maximum number of microseconds that clients may spend on logging in.
566
566
  * Clients that take longer are disconnected.
567
567
  *
568
568
  * @pre timeout != 0
data/ext/common/Process.h CHANGED
@@ -36,11 +36,10 @@
36
36
  #include <unistd.h>
37
37
  #include <errno.h>
38
38
 
39
- #include "Session.h"
40
- #include "MessageChannel.h"
41
- #include "Exceptions.h"
42
- #include "Logging.h"
43
- #include "Utils.h"
39
+ #include <Session.h>
40
+ #include <Exceptions.h>
41
+ #include <Logging.h>
42
+ #include <Utils.h>
44
43
 
45
44
  namespace Passenger {
46
45
 
data/ext/common/Session.h CHANGED
@@ -41,11 +41,11 @@
41
41
  #include <oxt/backtrace.hpp>
42
42
  #include <oxt/system_calls.hpp>
43
43
 
44
- #include "MessageChannel.h"
45
- #include "StaticString.h"
46
- #include "Exceptions.h"
47
- #include "Utils/StrIntUtils.h"
48
- #include "Utils/IOUtils.h"
44
+ #include <StaticString.h>
45
+ #include <Exceptions.h>
46
+ #include <Utils/StrIntUtils.h>
47
+ #include <Utils/IOUtils.h>
48
+ #include <Utils/MessageIO.h>
49
49
 
50
50
  namespace Passenger {
51
51
 
@@ -159,7 +159,7 @@ public:
159
159
  "because the I/O stream has already been closed or discarded.");
160
160
  }
161
161
  try {
162
- MessageChannel(stream).writeScalar(headers, size);
162
+ writeScalarMessage(stream, headers, size);
163
163
  } catch (SystemException &e) {
164
164
  e.setBriefMessage("An error occured while writing headers "
165
165
  "to the request handler");
@@ -222,32 +222,6 @@ public:
222
222
  */
223
223
  virtual int getStream() const = 0;
224
224
 
225
- /**
226
- * Set the timeout value for reading data from the I/O channel.
227
- * If no data can be read within the timeout period, then the
228
- * read call will fail with error EAGAIN or EWOULDBLOCK.
229
- *
230
- * @pre The I/O channel hasn't been closed or discarded.
231
- * @pre initiated()
232
- * @param msec The timeout, in milliseconds. If 0 is given,
233
- * there will be no timeout.
234
- * @throws SystemException Cannot set the timeout.
235
- */
236
- virtual void setReaderTimeout(unsigned int msec) = 0;
237
-
238
- /**
239
- * Set the timeout value for writing data from the I/O channel.
240
- * If no data can be written within the timeout period, then the
241
- * write call will fail with error EAGAIN or EWOULDBLOCK.
242
- *
243
- * @pre The I/O channel hasn't been closed or discarded.
244
- * @pre initiated()
245
- * @param msec The timeout, in milliseconds. If 0 is given,
246
- * there will be no timeout.
247
- * @throws SystemException Cannot set the timeout.
248
- */
249
- virtual void setWriterTimeout(unsigned int msec) = 0;
250
-
251
225
  /**
252
226
  * Indicate that we don't want to read data anymore from the I/O channel.
253
227
  * Calling this method after closeStream()/discardStream() is called will
@@ -397,14 +371,6 @@ public:
397
371
  return fd;
398
372
  }
399
373
 
400
- virtual void setReaderTimeout(unsigned int msec) {
401
- MessageChannel(fd).setReadTimeout(msec);
402
- }
403
-
404
- virtual void setWriterTimeout(unsigned int msec) {
405
- MessageChannel(fd).setWriteTimeout(msec);
406
- }
407
-
408
374
  virtual void shutdownReader() {
409
375
  TRACE_POINT();
410
376
  if (fd != -1) {
@@ -45,18 +45,18 @@
45
45
  #include <pwd.h>
46
46
  #include <signal.h>
47
47
 
48
- #include "AbstractSpawnManager.h"
49
- #include "ServerInstanceDir.h"
50
- #include "FileDescriptor.h"
51
- #include "Constants.h"
52
- #include "MessageChannel.h"
53
- #include "AccountsDatabase.h"
54
- #include "RandomGenerator.h"
55
- #include "Exceptions.h"
56
- #include "Logging.h"
57
- #include "Utils/Base64.h"
58
- #include "Utils/SystemTime.h"
59
- #include "Utils/IOUtils.h"
48
+ #include <AbstractSpawnManager.h>
49
+ #include <ServerInstanceDir.h>
50
+ #include <FileDescriptor.h>
51
+ #include <Constants.h>
52
+ #include <AccountsDatabase.h>
53
+ #include <RandomGenerator.h>
54
+ #include <Exceptions.h>
55
+ #include <Logging.h>
56
+ #include <Utils/Base64.h>
57
+ #include <Utils/SystemTime.h>
58
+ #include <Utils/IOUtils.h>
59
+ #include <Utils/MessageIO.h>
60
60
 
61
61
  namespace Passenger {
62
62
 
@@ -268,9 +268,8 @@ private:
268
268
  FileDescriptor connect() const {
269
269
  TRACE_POINT();
270
270
  FileDescriptor fd = connectToUnixServer(socketFilename.c_str());
271
- MessageChannel channel(fd);
272
271
  UPDATE_TRACE_POINT();
273
- channel.writeScalar(socketPassword);
272
+ writeScalarMessage(fd, socketPassword);
274
273
  return fd;
275
274
  }
276
275
 
@@ -286,13 +285,11 @@ private:
286
285
  ProcessPtr sendSpawnCommand(const PoolOptions &options) {
287
286
  TRACE_POINT();
288
287
  FileDescriptor connection;
289
- MessageChannel channel;
290
288
 
291
289
  P_DEBUG("Spawning a new application process for " << options.appRoot << "...");
292
290
 
293
291
  try {
294
292
  connection = connect();
295
- channel = MessageChannel(connection);
296
293
  } catch (const SystemException &e) {
297
294
  throw SpawnException(string("Could not connect to the spawn server: ") +
298
295
  e.sys());
@@ -337,7 +334,7 @@ private:
337
334
  args.push_back(Base64::encode(password));
338
335
  }
339
336
 
340
- channel.write(args);
337
+ writeArrayMessage(connection, args);
341
338
  } catch (const SystemException &e) {
342
339
  throw SpawnException(string("Could not write 'spawn_application' "
343
340
  "command to the spawn server: ") + e.sys());
@@ -346,7 +343,7 @@ private:
346
343
  try {
347
344
  UPDATE_TRACE_POINT();
348
345
  // Read status.
349
- if (!channel.read(args)) {
346
+ if (!readArrayMessage(connection, args)) {
350
347
  throw SpawnException("The spawn server has exited unexpectedly.");
351
348
  }
352
349
  if (args.size() != 1) {
@@ -356,7 +353,7 @@ private:
356
353
  UPDATE_TRACE_POINT();
357
354
  string errorPage;
358
355
 
359
- if (!channel.readScalar(errorPage)) {
356
+ if (!readScalarMessage(connection, errorPage)) {
360
357
  throw SpawnException("The spawn server has exited unexpectedly.");
361
358
  }
362
359
  throw SpawnException("An error occured while spawning the application.",
@@ -367,7 +364,7 @@ private:
367
364
 
368
365
  // Read application info.
369
366
  UPDATE_TRACE_POINT();
370
- if (!channel.read(args)) {
367
+ if (!readArrayMessage(connection, args)) {
371
368
  throw SpawnException("The spawn server has exited unexpectedly.");
372
369
  }
373
370
  if (args.size() != 3) {
@@ -380,7 +377,7 @@ private:
380
377
 
381
378
  UPDATE_TRACE_POINT();
382
379
  for (i = 0; i < nServerSockets; i++) {
383
- if (!channel.read(args)) {
380
+ if (!readArrayMessage(connection, args)) {
384
381
  throw SpawnException("The spawn server has exited unexpectedly.");
385
382
  }
386
383
  if (args.size() != 3) {
@@ -398,7 +395,7 @@ private:
398
395
 
399
396
  UPDATE_TRACE_POINT();
400
397
  try {
401
- ownerPipe = channel.readFileDescriptor();
398
+ ownerPipe = readFileDescriptorWithNegotiation(connection);
402
399
  } catch (const SystemException &e) {
403
400
  throw SpawnException(string("Could not receive the spawned "
404
401
  "application's owner pipe from the spawn server: ") +
@@ -454,11 +451,9 @@ private:
454
451
  void sendReloadCommand(const string &appRoot) {
455
452
  TRACE_POINT();
456
453
  FileDescriptor connection;
457
- MessageChannel channel;
458
454
 
459
455
  try {
460
456
  connection = connect();
461
- channel = MessageChannel(connection);
462
457
  } catch (SystemException &e) {
463
458
  e.setBriefMessage("Could not connect to the spawn server");
464
459
  throw;
@@ -468,7 +463,7 @@ private:
468
463
  }
469
464
 
470
465
  try {
471
- channel.write("reload", appRoot.c_str(), NULL);
466
+ writeArrayMessage(connection, "reload", appRoot.c_str(), NULL);
472
467
  } catch (SystemException &e) {
473
468
  e.setBriefMessage("Could not write 'reload' command to the spawn server");
474
469
  throw;
data/ext/common/Utils.cpp CHANGED
@@ -33,6 +33,7 @@
33
33
  #include <sys/types.h>
34
34
  #include <sys/wait.h>
35
35
  #include <sys/resource.h>
36
+ #include <sys/uio.h>
36
37
  #include <libgen.h>
37
38
  #include <fcntl.h>
38
39
  #include <poll.h>
@@ -41,7 +42,6 @@
41
42
  #include <unistd.h>
42
43
  #include <signal.h>
43
44
  #include <FileDescriptor.h>
44
- #include <MessageChannel.h>
45
45
  #include <MessageServer.h>
46
46
  #include <ResourceLocator.h>
47
47
  #include <Exceptions.h>
@@ -229,4 +229,4 @@ public:
229
229
 
230
230
  } // namespace Passenger
231
231
 
232
- #endif _PASSENGER_DECHUNKER_H_
232
+ #endif /* _PASSENGER_DECHUNKER_H_ */
@@ -300,26 +300,28 @@ readArrayMessage(int fd, unsigned long long *timeout = NULL) {
300
300
  * total number of microseconds spent on reading will be deducted
301
301
  * from <tt>timeout</tt>.
302
302
  * Pass NULL if you do not want to enforce a timeout.
303
- * @throws EOFException End-of-file was reached before a full integer could be read.
303
+ * @return True if a scalar message was read, false if EOF was encountered.
304
304
  * @throws SystemException Something went wrong.
305
305
  * @throws SecurityException The message body is larger than allowed by maxSize.
306
306
  * @throws TimeoutException Unable to read the necessary data within
307
307
  * <tt>timeout</tt> microseconds.
308
308
  * @throws boost::thread_interrupted
309
309
  */
310
- inline string
311
- readScalarMessage(int fd, unsigned int maxSize = 0, unsigned long long *timeout = NULL) {
310
+ inline bool
311
+ readScalarMessage(int fd, string &output, unsigned int maxSize = 0, unsigned long long *timeout = NULL) {
312
312
  uint32_t size;
313
313
  if (!readUint32(fd, size, timeout)) {
314
- throw EOFException("EOF encountered before a 32-bit scalar message header could be read");
314
+ return false;
315
315
  }
316
316
 
317
317
  if (maxSize != 0 && size > (uint32_t) maxSize) {
318
318
  throw SecurityException("The scalar message body is larger than the size limit");
319
319
  }
320
320
 
321
- string output;
322
321
  unsigned int remaining = size;
322
+ if (OXT_UNLIKELY(!output.empty())) {
323
+ output.clear();
324
+ }
323
325
  output.reserve(size);
324
326
  if (OXT_LIKELY(remaining > 0)) {
325
327
  char buf[1024 * 32];
@@ -329,13 +331,43 @@ readScalarMessage(int fd, unsigned int maxSize = 0, unsigned long long *timeout
329
331
  unsigned int blockSize = min((unsigned int) sizeof(buf), remaining);
330
332
 
331
333
  if (readExact(fd, buf, blockSize, timeout) != blockSize) {
332
- throw EOFException("EOF encountered before the full scalar message body could be read");
334
+ return false;
333
335
  }
334
336
  output.append(buf, blockSize);
335
337
  remaining -= blockSize;
336
338
  }
337
339
  }
338
- return output;
340
+ return true;
341
+ }
342
+
343
+ /**
344
+ * Reads a scalar message from the given file descriptor.
345
+ *
346
+ * @param maxSize The maximum number of bytes that may be read. If the
347
+ * scalar to read is larger than this, then a SecurityException
348
+ * will be thrown. Set to 0 for no size limit.
349
+ * @param timeout A pointer to an integer, which specifies the maximum number of
350
+ * microseconds that may be spent on reading the necessary data.
351
+ * If the timeout expired then TimeoutException will be thrown.
352
+ * If this function returns without throwing an exception, then the
353
+ * total number of microseconds spent on reading will be deducted
354
+ * from <tt>timeout</tt>.
355
+ * Pass NULL if you do not want to enforce a timeout.
356
+ * @throws EOFException End-of-file was reached before a full integer could be read.
357
+ * @throws SystemException Something went wrong.
358
+ * @throws SecurityException The message body is larger than allowed by maxSize.
359
+ * @throws TimeoutException Unable to read the necessary data within
360
+ * <tt>timeout</tt> microseconds.
361
+ * @throws boost::thread_interrupted
362
+ */
363
+ inline string
364
+ readScalarMessage(int fd, unsigned int maxSize = 0, unsigned long long *timeout = NULL) {
365
+ string output;
366
+ if (readScalarMessage(fd, output, maxSize, timeout)) {
367
+ return output;
368
+ } else {
369
+ throw EOFException("EOF encountered before a full scalar message could be read");
370
+ }
339
371
  }
340
372
 
341
373
 
@@ -402,7 +434,7 @@ writeUint32(int fd, uint32_t value, unsigned long long *timeout = NULL) {
402
434
  */
403
435
  template<typename Collection>
404
436
  inline void
405
- writeArrayMessage(int fd, const Collection &args, unsigned long long *timeout = NULL) {
437
+ writeArrayMessageEx(int fd, const Collection &args, unsigned long long *timeout = NULL) {
406
438
  typename Collection::const_iterator it, end = args.end();
407
439
  uint16_t bodySize = 0;
408
440
 
@@ -425,20 +457,78 @@ writeArrayMessage(int fd, const Collection &args, unsigned long long *timeout =
425
457
  writeExact(fd, data.get(), sizeof(uint16_t) + bodySize, timeout);
426
458
  }
427
459
 
460
+ inline void
461
+ writeArrayMessage(int fd, const vector<StaticString> &args, unsigned long long *timeout = NULL) {
462
+ writeArrayMessageEx(fd, args, timeout);
463
+ }
464
+
465
+ inline void
466
+ writeArrayMessage(int fd, const vector<string> &args, unsigned long long *timeout = NULL) {
467
+ writeArrayMessageEx(fd, args, timeout);
468
+ }
469
+
470
+ inline void
471
+ writeArrayMessage(int fd, const StaticString args[], unsigned int nargs, unsigned long long *timeout = NULL) {
472
+ unsigned int i;
473
+ uint16_t bodySize = 0;
474
+
475
+ for (i = 0; i < nargs; i++) {
476
+ bodySize += args[i].size() + 1;
477
+ }
478
+
479
+ scoped_array<char> data(new char[sizeof(uint16_t) + bodySize]);
480
+ uint16_t header = htons(bodySize);
481
+ memcpy(data.get(), &header, sizeof(uint16_t));
482
+
483
+ char *dataEnd = data.get() + sizeof(uint16_t);
484
+ for (i = 0; i < nargs; i++) {
485
+ memcpy(dataEnd, args[i].data(), args[i].size());
486
+ dataEnd += args[i].size();
487
+ *dataEnd = '\0';
488
+ dataEnd++;
489
+ }
490
+
491
+ writeExact(fd, data.get(), sizeof(uint16_t) + bodySize, timeout);
492
+ }
493
+
428
494
  inline void
429
495
  writeArrayMessage(int fd, const StaticString &name, va_list &ap, unsigned long long *timeout = NULL) {
430
- vector<StaticString> args;
496
+ StaticString args[10];
497
+ unsigned int nargs = 1;
498
+ bool done = false;
431
499
 
432
- args.push_back(name);
433
- while (true) {
500
+ args[0] = name;
501
+ do {
434
502
  const char *arg = va_arg(ap, const char *);
435
503
  if (arg == NULL) {
436
- break;
504
+ done = true;
437
505
  } else {
438
- args.push_back(arg);
506
+ args[nargs] = arg;
507
+ nargs++;
439
508
  }
509
+ } while (!done && nargs < sizeof(args) / sizeof(StaticString));
510
+
511
+ if (done) {
512
+ writeArrayMessage(fd, args, nargs, timeout);
513
+ } else {
514
+ // Arguments don't fit in static array. Use dynamic
515
+ // array instead.
516
+ vector<StaticString> dyn_args;
517
+
518
+ for (unsigned int i = 0; i < nargs; i++) {
519
+ dyn_args.push_back(args[i]);
520
+ }
521
+ do {
522
+ const char *arg = va_arg(ap, const char *);
523
+ if (arg == NULL) {
524
+ done = true;
525
+ } else {
526
+ dyn_args.push_back(arg);
527
+ }
528
+ } while (!done);
529
+
530
+ writeArrayMessage(fd, dyn_args, timeout);
440
531
  }
441
- writeArrayMessage(fd, args, timeout);
442
532
  }
443
533
 
444
534
  struct _VaGuard {
@@ -464,6 +554,11 @@ writeArrayMessage(int fd, const StaticString &name, ...) {
464
554
  writeArrayMessage(fd, name, ap);
465
555
  }
466
556
 
557
+ inline void
558
+ writeArrayMessage(int fd, const char *name) {
559
+ abort();
560
+ }
561
+
467
562
  /** Version of writeArrayMessage() that accepts a variadic list of 'const char *'
468
563
  * arguments as message elements, with timeout support. The list must be terminated
469
564
  * with a NULL.