passenger 4.0.27 → 4.0.28

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 (156) hide show
  1. data.tar.gz.asc +7 -7
  2. data/.gitignore +1 -0
  3. data/NEWS +22 -0
  4. data/build/preprocessor.rb +10 -0
  5. data/build/rpm.rb +74 -65
  6. data/debian.template/rules.template +8 -0
  7. data/dev/copy_boost_headers.rb +11 -2
  8. data/doc/Users guide Apache.idmap.txt +161 -145
  9. data/doc/Users guide Apache.txt +12 -1
  10. data/doc/Users guide Nginx.idmap.txt +142 -126
  11. data/doc/Users guide Nginx.txt +14 -1
  12. data/doc/Users guide Standalone.txt +1 -0
  13. data/doc/users_guide_snippets/environment_variables.txt +1 -1
  14. data/doc/users_guide_snippets/installation.txt +2 -0
  15. data/doc/users_guide_snippets/tips.txt +118 -0
  16. data/ext/apache2/Configuration.cpp +0 -6
  17. data/ext/apache2/Configuration.hpp +0 -5
  18. data/ext/apache2/ConfigurationCommands.cpp +7 -0
  19. data/ext/apache2/ConfigurationFields.hpp +2 -0
  20. data/ext/apache2/ConfigurationSetters.cpp +24 -0
  21. data/ext/apache2/CreateDirConfig.cpp +1 -0
  22. data/ext/apache2/Hooks.cpp +0 -1
  23. data/ext/apache2/MergeDirConfig.cpp +7 -0
  24. data/ext/apache2/SetHeaders.cpp +5 -1
  25. data/ext/boost/cregex.hpp +39 -0
  26. data/ext/boost/libs/regex/src/c_regex_traits.cpp +193 -0
  27. data/ext/boost/libs/regex/src/cpp_regex_traits.cpp +117 -0
  28. data/ext/boost/libs/regex/src/cregex.cpp +660 -0
  29. data/ext/boost/libs/regex/src/instances.cpp +32 -0
  30. data/ext/boost/libs/regex/src/internals.hpp +35 -0
  31. data/ext/boost/libs/regex/src/posix_api.cpp +296 -0
  32. data/ext/boost/libs/regex/src/regex.cpp +227 -0
  33. data/ext/boost/libs/regex/src/regex_debug.cpp +59 -0
  34. data/ext/boost/libs/regex/src/regex_raw_buffer.cpp +72 -0
  35. data/ext/boost/libs/regex/src/regex_traits_defaults.cpp +692 -0
  36. data/ext/boost/libs/regex/src/static_mutex.cpp +179 -0
  37. data/ext/boost/libs/regex/src/wc_regex_traits.cpp +301 -0
  38. data/ext/boost/libs/regex/src/wide_posix_api.cpp +315 -0
  39. data/ext/boost/libs/regex/src/winstances.cpp +35 -0
  40. data/ext/boost/regex.h +100 -0
  41. data/ext/boost/regex.hpp +37 -0
  42. data/ext/boost/regex/concepts.hpp +1128 -0
  43. data/ext/boost/regex/config.hpp +435 -0
  44. data/ext/boost/regex/config/borland.hpp +72 -0
  45. data/ext/boost/regex/config/cwchar.hpp +207 -0
  46. data/ext/boost/regex/mfc.hpp +190 -0
  47. data/ext/boost/regex/pattern_except.hpp +100 -0
  48. data/ext/boost/regex/pending/object_cache.hpp +165 -0
  49. data/ext/boost/regex/pending/static_mutex.hpp +179 -0
  50. data/ext/boost/regex/pending/unicode_iterator.hpp +776 -0
  51. data/ext/boost/regex/regex_traits.hpp +35 -0
  52. data/ext/boost/regex/user.hpp +93 -0
  53. data/ext/boost/regex/v4/basic_regex.hpp +782 -0
  54. data/ext/boost/regex/v4/basic_regex_creator.hpp +1571 -0
  55. data/ext/boost/regex/v4/basic_regex_parser.hpp +2874 -0
  56. data/ext/boost/regex/v4/c_regex_traits.hpp +211 -0
  57. data/ext/boost/regex/v4/char_regex_traits.hpp +81 -0
  58. data/ext/boost/regex/v4/cpp_regex_traits.hpp +1099 -0
  59. data/ext/boost/regex/v4/cregex.hpp +330 -0
  60. data/ext/boost/regex/v4/error_type.hpp +59 -0
  61. data/ext/boost/regex/v4/fileiter.hpp +455 -0
  62. data/ext/boost/regex/v4/instances.hpp +222 -0
  63. data/ext/boost/regex/v4/iterator_category.hpp +91 -0
  64. data/ext/boost/regex/v4/iterator_traits.hpp +135 -0
  65. data/ext/boost/regex/v4/match_flags.hpp +138 -0
  66. data/ext/boost/regex/v4/match_results.hpp +702 -0
  67. data/ext/boost/regex/v4/mem_block_cache.hpp +99 -0
  68. data/ext/boost/regex/v4/perl_matcher.hpp +587 -0
  69. data/ext/boost/regex/v4/perl_matcher_common.hpp +996 -0
  70. data/ext/boost/regex/v4/perl_matcher_non_recursive.hpp +1642 -0
  71. data/ext/boost/regex/v4/perl_matcher_recursive.hpp +991 -0
  72. data/ext/boost/regex/v4/primary_transform.hpp +146 -0
  73. data/ext/boost/regex/v4/protected_call.hpp +81 -0
  74. data/ext/boost/regex/v4/regbase.hpp +180 -0
  75. data/ext/boost/regex/v4/regex.hpp +202 -0
  76. data/ext/boost/regex/v4/regex_format.hpp +1156 -0
  77. data/ext/boost/regex/v4/regex_fwd.hpp +73 -0
  78. data/ext/boost/regex/v4/regex_grep.hpp +155 -0
  79. data/ext/boost/regex/v4/regex_iterator.hpp +201 -0
  80. data/ext/boost/regex/v4/regex_match.hpp +382 -0
  81. data/ext/boost/regex/v4/regex_merge.hpp +93 -0
  82. data/ext/boost/regex/v4/regex_raw_buffer.hpp +210 -0
  83. data/ext/boost/regex/v4/regex_replace.hpp +99 -0
  84. data/ext/boost/regex/v4/regex_search.hpp +217 -0
  85. data/ext/boost/regex/v4/regex_split.hpp +172 -0
  86. data/ext/boost/regex/v4/regex_token_iterator.hpp +342 -0
  87. data/ext/boost/regex/v4/regex_traits.hpp +189 -0
  88. data/ext/boost/regex/v4/regex_traits_defaults.hpp +371 -0
  89. data/ext/boost/regex/v4/regex_workaround.hpp +232 -0
  90. data/ext/boost/regex/v4/states.hpp +301 -0
  91. data/ext/boost/regex/v4/sub_match.hpp +512 -0
  92. data/ext/boost/regex/v4/syntax_type.hpp +105 -0
  93. data/ext/boost/regex/v4/u32regex_iterator.hpp +193 -0
  94. data/ext/boost/regex/v4/u32regex_token_iterator.hpp +377 -0
  95. data/ext/boost/regex/v4/w32_regex_traits.hpp +741 -0
  96. data/ext/boost/regex_fwd.hpp +33 -0
  97. data/ext/common/AgentsStarter.h +0 -11
  98. data/ext/common/ApplicationPool2/Common.h +1 -7
  99. data/ext/common/ApplicationPool2/DirectSpawner.h +3 -3
  100. data/ext/common/ApplicationPool2/Group.h +166 -69
  101. data/ext/common/ApplicationPool2/Implementation.cpp +55 -10
  102. data/ext/common/ApplicationPool2/Options.h +45 -10
  103. data/ext/common/ApplicationPool2/PipeWatcher.h +1 -2
  104. data/ext/common/ApplicationPool2/Pool.h +29 -7
  105. data/ext/common/ApplicationPool2/Process.h +22 -3
  106. data/ext/common/ApplicationPool2/Session.h +1 -0
  107. data/ext/common/ApplicationPool2/SmartSpawner.h +5 -10
  108. data/ext/common/ApplicationPool2/Spawner.h +10 -15
  109. data/ext/common/ApplicationPool2/SuperGroup.h +10 -9
  110. data/ext/common/Constants.h +1 -3
  111. data/ext/common/Hooks.h +193 -0
  112. data/ext/common/Logging.cpp +67 -2
  113. data/ext/common/Logging.h +23 -1
  114. data/ext/common/Utils.cpp +0 -21
  115. data/ext/common/Utils.h +0 -42
  116. data/ext/common/Utils/CachedFileStat.hpp +1 -1
  117. data/ext/common/Utils/StrIntUtils.h +61 -14
  118. data/ext/common/Utils/StringMap.h +4 -0
  119. data/ext/common/agents/HelperAgent/AgentOptions.h +4 -4
  120. data/ext/common/agents/HelperAgent/Main.cpp +2 -3
  121. data/ext/common/agents/HelperAgent/RequestHandler.h +65 -2
  122. data/ext/common/agents/LoggingAgent/FilterSupport.h +3 -1
  123. data/ext/common/agents/Watchdog/Main.cpp +8 -72
  124. data/ext/nginx/CacheLocationConfig.c +29 -1
  125. data/ext/nginx/Configuration.c +0 -12
  126. data/ext/nginx/Configuration.h +0 -1
  127. data/ext/nginx/ConfigurationCommands.c +10 -0
  128. data/ext/nginx/ConfigurationFields.h +2 -0
  129. data/ext/nginx/CreateLocationConfig.c +4 -0
  130. data/ext/nginx/MergeLocationConfig.c +6 -0
  131. data/ext/oxt/system_calls.cpp +7 -1
  132. data/ext/oxt/system_calls.hpp +7 -7
  133. data/helper-scripts/node-loader.js +6 -2
  134. data/helper-scripts/rack-loader.rb +5 -2
  135. data/helper-scripts/rack-preloader.rb +5 -2
  136. data/lib/phusion_passenger.rb +1 -1
  137. data/lib/phusion_passenger/apache2/config_options.rb +8 -0
  138. data/lib/phusion_passenger/constants.rb +0 -1
  139. data/lib/phusion_passenger/nginx/config_options.rb +9 -2
  140. data/lib/phusion_passenger/platform_info/apache.rb +2 -1
  141. data/lib/phusion_passenger/platform_info/compiler.rb +15 -1
  142. data/lib/phusion_passenger/platform_info/cxx_portability.rb +2 -0
  143. data/node_lib/phusion_passenger/httplib_emulation.js +85 -17
  144. data/node_lib/phusion_passenger/request_handler.js +10 -2
  145. data/rpm/Vagrantfile +32 -0
  146. data/rpm/get_distro_id.py +4 -0
  147. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +2 -2
  148. data/test/cxx/ApplicationPool2/PoolTest.cpp +60 -9
  149. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +2 -6
  150. data/test/cxx/CachedFileStatTest.cpp +5 -5
  151. data/test/cxx/RequestHandlerTest.cpp +3 -6
  152. data/test/cxx/UtilsTest.cpp +30 -0
  153. data/test/node/httplib_emulation_spec.js +491 -0
  154. data/test/node/spec_helper.js +25 -0
  155. metadata +78 -2
  156. metadata.gz.asc +7 -7
@@ -124,7 +124,29 @@ enum PassengerLogLevel {
124
124
  #define P_TRACE(level, expr) do { /* nothing */ } while (false)
125
125
  #endif
126
126
 
127
+ /**
128
+ * Print a message that was received from an application's stdout/stderr.
129
+ *
130
+ * @param pid The application's PID.
131
+ * @param channelName "stdout" or "stderr".
132
+ * @param message The message that was received.
133
+ */
134
+ void printAppOutput(pid_t pid, const char *channelName, const char *message, unsigned int size);
135
+
136
+ /**
137
+ * Controls how messages that are received from applications are printed.
138
+ *
139
+ * If `enabled` is true then messages are printed using P_DEBUG, meaning that
140
+ * the normal Passenger logging prefixes will be printed as well.
141
+ *
142
+ * If `enabled` is false (the default), then messages are printed directly
143
+ * to the log output channel using write(), with only a very short prefix
144
+ * that contains the PID and channel name.
145
+ */
146
+ void setPrintAppOutputAsDebuggingMessages(bool enabled);
147
+
127
148
 
149
+ /** Print a [BUG] error message and abort with a stack trace. */
128
150
  #define P_BUG(expr) \
129
151
  do { \
130
152
  TRACE_POINT(); \
@@ -165,7 +187,7 @@ public:
165
187
 
166
188
  /**
167
189
  * Put this in code sections where you don't expect *any* exceptions to be thrown.
168
- * This macro will automatically disables interruptions in the current scope,
190
+ * This macro will automatically disable interruptions in the current scope,
169
191
  * and will print an error message whenever the scope exits with an exception.
170
192
  *
171
193
  * When inside critical sections, you should put this macro right after the lock
@@ -731,27 +731,6 @@ removeDirTree(const string &path) {
731
731
  }
732
732
  }
733
733
 
734
- bool
735
- verifyRailsDir(const string &dir, CachedFileStat *cstat, unsigned int throttleRate) {
736
- string temp(dir);
737
- temp.append("/config/environment.rb");
738
- return fileExists(temp, cstat, throttleRate);
739
- }
740
-
741
- bool
742
- verifyRackDir(const string &dir, CachedFileStat *cstat, unsigned int throttleRate) {
743
- string temp(dir);
744
- temp.append("/config.ru");
745
- return fileExists(temp, cstat, throttleRate);
746
- }
747
-
748
- bool
749
- verifyWSGIDir(const string &dir, CachedFileStat *cstat, unsigned int throttleRate) {
750
- string temp(dir);
751
- temp.append("/passenger_wsgi.py");
752
- return fileExists(temp, cstat, throttleRate);
753
- }
754
-
755
734
  void
756
735
  prestartWebApps(const ResourceLocator &locator, const string &ruby,
757
736
  const vector<string> &prestartURLs)
@@ -337,48 +337,6 @@ void makeDirTree(const string &path, const StaticString &mode = "u=rwx,g=,o=",
337
337
  */
338
338
  void removeDirTree(const string &path);
339
339
 
340
- /**
341
- * Check whether the specified directory is a valid Ruby on Rails
342
- * application root directory.
343
- *
344
- * @param cstat A CachedFileStat object, if you want to use cached statting.
345
- * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL.
346
- * @throws FileSystemException Unable to check because of a system error.
347
- * @throws TimeRetrievalException
348
- * @throws boost::thread_interrupted
349
- * @ingroup Support
350
- */
351
- bool verifyRailsDir(const string &dir, CachedFileStat *cstat = 0,
352
- unsigned int throttleRate = 0);
353
-
354
- /**
355
- * Check whether the specified directory is a valid Rack application
356
- * root directory.
357
- *
358
- * @param cstat A CachedFileStat object, if you want to use cached statting.
359
- * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL.
360
- * @throws FileSystemException Unable to check because of a filesystem error.
361
- * @throws TimeRetrievalException
362
- * @throws boost::thread_interrupted
363
- * @ingroup Support
364
- */
365
- bool verifyRackDir(const string &dir, CachedFileStat *cstat = 0,
366
- unsigned int throttleRate = 0);
367
-
368
- /**
369
- * Check whether the specified directory is a valid WSGI application
370
- * root directory.
371
- *
372
- * @param cstat A CachedFileStat object, if you want to use cached statting.
373
- * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL.
374
- * @throws FileSystemException Unable to check because of a filesystem error.
375
- * @throws TimeRetrievalException
376
- * @throws boost::thread_interrupted
377
- * @ingroup Support
378
- */
379
- bool verifyWSGIDir(const string &dir, CachedFileStat *cstat = 0,
380
- unsigned int throttleRate = 0);
381
-
382
340
  void prestartWebApps(const ResourceLocator &locator, const string &ruby,
383
341
  const vector<string> &prestartURLs);
384
342
 
@@ -237,7 +237,7 @@ public:
237
237
  */
238
238
  bool knows(const StaticString &filename) const {
239
239
  boost::unique_lock<boost::mutex> l(lock);
240
- return cache.get(filename) != EntryList::iterator();
240
+ return cache.has(filename);
241
241
  }
242
242
  };
243
243
 
@@ -28,6 +28,8 @@
28
28
  #include <string>
29
29
  #include <vector>
30
30
  #include <sstream>
31
+ #include <stdexcept>
32
+ #include <new>
31
33
  #include <cstddef>
32
34
  #include <ctime>
33
35
  #include <oxt/macros.hpp>
@@ -38,6 +40,34 @@ namespace Passenger {
38
40
  using namespace std;
39
41
 
40
42
 
43
+ /**
44
+ * A RAII construct for memory buffers that are dynamically allocated with malloc().
45
+ * Upon destruction of a DynamicBuffer, the memory buffer is freed.
46
+ */
47
+ struct DynamicBuffer {
48
+ typedef string::size_type size_type;
49
+
50
+ char *data;
51
+ size_type size;
52
+
53
+ /**
54
+ * @throws std::bad_alloc The buffer cannot be allocated.
55
+ */
56
+ DynamicBuffer(size_type _size)
57
+ : size(_size)
58
+ {
59
+ data = (char *) malloc(_size);
60
+ if (data == NULL) {
61
+ throw std::bad_alloc();
62
+ }
63
+ }
64
+
65
+ ~DynamicBuffer() throw() {
66
+ free(data);
67
+ }
68
+ };
69
+
70
+
41
71
  /**
42
72
  * Given a prefix string, a middle string and a postfix string, try to build a string
43
73
  * that looks like <tt>prefix + middle + postfix</tt>, with as many characters from
@@ -162,39 +192,56 @@ string toHex(const StaticString &data);
162
192
  */
163
193
  void toHex(const StaticString & restrict_ref data, char * restrict output, bool upperCase = false);
164
194
 
195
+ /**
196
+ * Reverse a string in-place.
197
+ */
198
+ inline void
199
+ reverseString(char *str, unsigned int size) {
200
+ char *end = str + size;
201
+ for (--end; str < end; str++, end--) {
202
+ *str = *str ^ *end,
203
+ *end = *str ^ *end,
204
+ *str = *str ^ *end;
205
+ }
206
+ }
207
+
165
208
  /**
166
209
  * Convert the given integer to some other radix, placing
167
- * the result into the given output buffer. This buffer must be at
168
- * least <tt>2 * sizeof(IntegerType) + 1</tt> bytes. The output buffer
210
+ * the result into the given output buffer. The output buffer
169
211
  * will be NULL terminated. Supported radices are 2-36.
170
- *
212
+ *
213
+ * @param outputSize The size of the output buffer, including space for
214
+ * the terminating NULL.
171
215
  * @return The size of the created string, excluding
172
216
  * terminating NULL.
217
+ * @throws std::length_error The output buffer is not large enough.
173
218
  */
174
219
  template<typename IntegerType, int radix>
175
220
  unsigned int
176
- integerToOtherBase(IntegerType value, char *output) {
177
- static const char hex_chars[] = {
221
+ integerToOtherBase(IntegerType value, char *output, unsigned int outputSize) {
222
+ static const char chars[] = {
178
223
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
179
224
  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
180
225
  'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
181
226
  'u', 'v', 'w', 'x', 'y', 'z'
182
227
  };
183
- char buf[sizeof(value) * 2];
184
228
  IntegerType remainder = value;
185
229
  unsigned int size = 0;
186
230
 
187
231
  do {
188
- buf[size] = hex_chars[remainder % radix];
232
+ output[size] = chars[remainder % radix];
189
233
  remainder = remainder / radix;
190
234
  size++;
191
- } while (remainder != 0);
235
+ } while (remainder != 0 && size < outputSize - 1);
192
236
 
193
- for (unsigned int i = 0; i < size; i++) {
194
- output[size - i - 1] = buf[i];
237
+ if (remainder == 0) {
238
+ reverseString(output, size);
239
+ output[size] = '\0';
240
+ return size;
241
+ } else {
242
+ throw std::length_error("Buffer not large enough to for integerToOtherBase()");
243
+ return -1; // Shut up compiler warning.
195
244
  }
196
- output[size] = '\0';
197
- return size;
198
245
  }
199
246
 
200
247
  /**
@@ -209,7 +256,7 @@ integerToOtherBase(IntegerType value, char *output) {
209
256
  template<typename IntegerType>
210
257
  unsigned int
211
258
  integerToHex(IntegerType value, char *output) {
212
- return integerToOtherBase<IntegerType, 16>(value, output);
259
+ return integerToOtherBase<IntegerType, 16>(value, output, 2 * sizeof(IntegerType) + 1);
213
260
  }
214
261
 
215
262
  /**
@@ -229,7 +276,7 @@ string integerToHex(long long value);
229
276
  template<typename IntegerType>
230
277
  unsigned int
231
278
  integerToHexatri(IntegerType value, char *output) {
232
- return integerToOtherBase<IntegerType, 36>(value, output);
279
+ return integerToOtherBase<IntegerType, 36>(value, output, 2 * sizeof(IntegerType) + 1);
233
280
  }
234
281
 
235
282
  /**
@@ -170,6 +170,10 @@ public:
170
170
  return it->second.thePair.second;
171
171
  }
172
172
  }
173
+
174
+ bool has(const StaticString &key) const {
175
+ return store.find(key) != store.end();
176
+ }
173
177
 
174
178
  bool set(const StaticString &key, const T &value) {
175
179
  pair<InternalIterator, bool> result = store.insert(make_pair(key, Entry()));
@@ -36,7 +36,7 @@ using namespace std;
36
36
  using namespace boost;
37
37
 
38
38
 
39
- struct AgentOptions {
39
+ struct AgentOptions: public VariantMap {
40
40
  pid_t webServerPid;
41
41
  string serverInstanceDir;
42
42
  string tempDir;
@@ -47,7 +47,6 @@ struct AgentOptions {
47
47
  string defaultRubyCommand;
48
48
  unsigned int generationNumber;
49
49
  unsigned int maxPoolSize;
50
- unsigned int maxInstancesPerApp;
51
50
  unsigned int poolIdleTime;
52
51
  string requestSocketFilename;
53
52
  string requestSocketPassword;
@@ -63,7 +62,9 @@ struct AgentOptions {
63
62
 
64
63
  AgentOptions() { }
65
64
 
66
- AgentOptions(const VariantMap &options) {
65
+ AgentOptions(const VariantMap &options)
66
+ : VariantMap(options)
67
+ {
67
68
  testBinary = options.get("test_binary", false) == "1";
68
69
  if (testBinary) {
69
70
  return;
@@ -77,7 +78,6 @@ struct AgentOptions {
77
78
  defaultUser = options.get("default_user");
78
79
  defaultGroup = options.get("default_group");
79
80
  maxPoolSize = options.getInt("max_pool_size");
80
- maxInstancesPerApp = options.getInt("max_instances_per_app");
81
81
  poolIdleTime = options.getInt("pool_idle_time");
82
82
 
83
83
  // Required options only set by the Watchdog.
@@ -450,11 +450,10 @@ public:
450
450
  "logging", options.loggingAgentPassword);
451
451
  spawnerFactory = boost::make_shared<SpawnerFactory>(poolLoop.safe,
452
452
  resourceLocator, generation, boost::make_shared<SpawnerConfig>(randomGenerator));
453
- pool = boost::make_shared<Pool>(poolLoop.safe.get(), spawnerFactory, loggerFactory,
454
- randomGenerator);
453
+ pool = boost::make_shared<Pool>(spawnerFactory, loggerFactory,
454
+ randomGenerator, &options);
455
455
  pool->initialize();
456
456
  pool->setMax(options.maxPoolSize);
457
- //pool->setMaxPerApp(maxInstancesPerApp);
458
457
  pool->setMaxIdleTime(options.poolIdleTime * 1000000);
459
458
 
460
459
  requestHandler = boost::make_shared<RequestHandler>(requestLoop.safe,
@@ -90,6 +90,7 @@
90
90
  #include <sys/types.h>
91
91
  #include <arpa/inet.h>
92
92
  #include <sys/un.h>
93
+ #include <utility>
93
94
  #include <typeinfo>
94
95
  #include <cassert>
95
96
  #include <cctype>
@@ -184,6 +185,7 @@ private:
184
185
  contentLength = 0;
185
186
  clientBodyAlreadyRead = 0;
186
187
  checkoutSessionAfterCommit = false;
188
+ stickySession = false;
187
189
  sessionCheckedOut = false;
188
190
  sessionCheckoutTry = 0;
189
191
  responseHeaderSeen = false;
@@ -283,6 +285,7 @@ public:
283
285
  bool requestBodyIsBuffered;
284
286
  bool sessionCheckedOut;
285
287
  bool checkoutSessionAfterCommit;
288
+ bool stickySession;
286
289
 
287
290
  bool responseHeaderSeen;
288
291
  bool chunkedResponse;
@@ -1044,6 +1047,16 @@ private:
1044
1047
  headerData.append("X-Powered-By: Phusion Passenger\r\n");
1045
1048
  }
1046
1049
 
1050
+ // Add sticky session ID.
1051
+ if (client->stickySession && client->session != NULL) {
1052
+ StaticString cookieName = getStickySessionCookieName(client);
1053
+ headerData.append("Set-Cookie: ");
1054
+ headerData.append(cookieName.data(), cookieName.size());
1055
+ headerData.append("=");
1056
+ headerData.append(toString(client->session->getStickySessionId()));
1057
+ headerData.append("; HttpOnly\r\n");
1058
+ }
1059
+
1047
1060
  // Add Date header. https://code.google.com/p/phusion-passenger/issues/detail?id=485
1048
1061
  if (lookupHeader(headerData, "Date", "date").empty()) {
1049
1062
  char dateStr[60];
@@ -1341,7 +1354,7 @@ private:
1341
1354
  } else {
1342
1355
  ClientPtr client = boost::make_shared<Client>();
1343
1356
  client->associate(this, fd);
1344
- clients.insert(make_pair<int, ClientPtr>(fd, client));
1357
+ clients.insert(make_pair((int) fd, client));
1345
1358
  acceptedClients[count] = client;
1346
1359
  count++;
1347
1360
  RH_DEBUG(client, "New client accepted; new client count = " << clients.size());
@@ -1721,7 +1734,8 @@ private:
1721
1734
  fillPoolOption(client, options.nodejs, "PASSENGER_NODEJS");
1722
1735
  fillPoolOption(client, options.user, "PASSENGER_USER");
1723
1736
  fillPoolOption(client, options.group, "PASSENGER_GROUP");
1724
- fillPoolOption(client, options.minProcesses, "PASSENGER_MIN_INSTANCES");
1737
+ fillPoolOption(client, options.minProcesses, "PASSENGER_MIN_PROCESSES");
1738
+ fillPoolOption(client, options.maxProcesses, "PASSENGER_MAX_PROCESSES");
1725
1739
  fillPoolOption(client, options.maxRequests, "PASSENGER_MAX_REQUESTS");
1726
1740
  fillPoolOption(client, options.spawnMethod, "PASSENGER_SPAWN_METHOD");
1727
1741
  fillPoolOption(client, options.startCommand, "PASSENGER_START_COMMAND");
@@ -1734,6 +1748,8 @@ private:
1734
1748
  fillPoolOption(client, options.loadShellEnvvars, "PASSENGER_LOAD_SHELL_ENVVARS");
1735
1749
  fillPoolOption(client, options.debugger, "PASSENGER_DEBUGGER");
1736
1750
  fillPoolOption(client, options.raiseInternalError, "PASSENGER_RAISE_INTERNAL_ERROR");
1751
+ setStickySessionId(client);
1752
+ /******************/
1737
1753
 
1738
1754
  for (it = client->scgiParser.begin(); it != end; it++) {
1739
1755
  if (!startsWith(it->first, "PASSENGER_")
@@ -1788,6 +1804,53 @@ private:
1788
1804
  }
1789
1805
  }
1790
1806
 
1807
+ void setStickySessionId(const ClientPtr &client) {
1808
+ ScgiRequestParser &parser = client->scgiParser;
1809
+ if (parser.getHeader("PASSENGER_STICKY_SESSION") == "true") {
1810
+ // TODO: This is not entirely correct. Clients MAY send multiple Cookie
1811
+ // headers, although this is in practice extremely rare.
1812
+ // http://stackoverflow.com/questions/16305814/are-multiple-cookie-headers-allowed-in-an-http-request
1813
+ StaticString cookie = parser.getHeader("HTTP_COOKIE");
1814
+ StaticString cookieName = getStickySessionCookieName(client);
1815
+ vector<StaticString> parts;
1816
+
1817
+ client->stickySession = true;
1818
+ split(cookie, ';', parts);
1819
+ foreach (StaticString part, parts) {
1820
+ const char *begin = part.data();
1821
+ const char *end = part.data() + part.size();
1822
+ const char *sep;
1823
+
1824
+ // Skip leading whitespace in the name.
1825
+ while (begin < end && *begin == ' ') {
1826
+ begin++;
1827
+ }
1828
+ part = StaticString(begin, end - begin);
1829
+
1830
+ // Find the separator ('=').
1831
+ sep = (const char *) memchr(begin, '=', end - begin);
1832
+ if (sep != NULL) {
1833
+ StaticString name(begin, sep - begin);
1834
+ if (name == cookieName) {
1835
+ // This cookie matches the one we're looking for.
1836
+ StaticString value(sep + 1, end - (sep + 1));
1837
+ client->options.stickySessionId = stringToUint(value);
1838
+ return;
1839
+ }
1840
+ }
1841
+ }
1842
+ }
1843
+ }
1844
+
1845
+ StaticString getStickySessionCookieName(const ClientPtr &client) const {
1846
+ StaticString value = client->scgiParser.getHeader("PASSENGER_STICKY_SESSION_COOKIE_NAME");
1847
+ if (value.empty()) {
1848
+ return StaticString("_passenger_route", sizeof("_passenger_route") - 1);
1849
+ } else {
1850
+ return value;
1851
+ }
1852
+ }
1853
+
1791
1854
  size_t state_readingHeader_onClientData(const ClientPtr &client, const char *data, size_t size) {
1792
1855
  ScgiRequestParser &parser = client->scgiParser;
1793
1856
  size_t consumed = parser.feed(data, size);
@@ -33,8 +33,10 @@
33
33
 
34
34
  #include <string>
35
35
  #include <set>
36
+ // Checking for _PCREPOSIX_H avoids conflicts with headers provided by Apache.
37
+ // https://code.google.com/p/phusion-passenger/issues/detail?id=651
36
38
  #ifndef _PCREPOSIX_H
37
- #include <regex.h>
39
+ #include <boost/regex.h>
38
40
  #endif
39
41
  #include <cstdio>
40
42
  #include <cstring>