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
@@ -153,6 +153,8 @@ private
153
153
  flags << "-DPASSENGER_DEBUG -DBOOST_DISABLE_ASSERTS"
154
154
 
155
155
  if cc_or_cxx == :cxx
156
+ flags << cxx_11_flag if cxx_11_flag
157
+
156
158
  # There are too many implementations of of the hash map!
157
159
  # Figure out the right one.
158
160
  check_unordered_map(flags, "std::unordered_map", "unordered_map", "HAS_UNORDERED_MAP") ||
@@ -81,15 +81,13 @@ function createIncomingMessage(headers, socket, bodyBegin) {
81
81
  message.url = headers['REQUEST_URI'];
82
82
  message.connection.remoteAddress = headers['REMOTE_ADDR'];
83
83
  message.connection.remotePort = parseInt(headers['REMOTE_PORT']);
84
+ message._mayHaveRequestBody = mayHaveRequestBody(headers);
85
+ message._emitEndEvent = IncomingMessage_emitEndEvent;
86
+ resetIncomingMessageOverridedMethods(message);
84
87
 
85
- function onSocketData(chunk) {
86
- message.emit('data', chunk);
87
- }
88
-
89
- function onSocketEnd() {
90
- message.emit('end');
91
- }
92
-
88
+ socket.on('end', function() {
89
+ message._emitEndEvent();
90
+ });
93
91
  socket.on('drain', function() {
94
92
  message.emit('drain');
95
93
  });
@@ -104,23 +102,93 @@ function createIncomingMessage(headers, socket, bodyBegin) {
104
102
  * to have a request body. For compatibility reasons we implement the
105
103
  * same behavior as Node's HTTP parser.
106
104
  */
107
- if (mayHaveRequestBody(headers)) {
108
- socket.on('data', onSocketData);
109
- socket.on('end', onSocketEnd);
105
+ if (message._mayHaveRequestBody) {
110
106
  if (bodyBegin.length > 0) {
111
- process.nextTick(function() {
112
- // TODO: we should check here whether the socket hasn't already been closed
113
- socket.emit('data', bodyBegin);
114
- });
107
+ message.push(bodyBegin);
108
+ }
109
+ socket.ondata = function(buffer, offset, end) {
110
+ if (!message.push(buffer.slice(offset, end))) {
111
+ socket._handle.readStop();
112
+ }
115
113
  }
116
114
  } else {
117
- // TODO: we should check in the next tick whether the socket hasn't already been closed
118
- process.nextTick(onSocketEnd);
115
+ message.push(null);
119
116
  }
120
117
 
121
118
  return message;
122
119
  }
123
120
 
121
+ function IncomingMessage_pause() {
122
+ this._flowing = false;
123
+ this._orig_pause();
124
+ resetIncomingMessageOverridedMethods(this);
125
+ }
126
+
127
+ function IncomingMessage_resume() {
128
+ this._flowing = true;
129
+ this._orig_resume();
130
+ resetIncomingMessageOverridedMethods(this);
131
+ }
132
+
133
+ function IncomingMessage_on(event, listener) {
134
+ if (event == 'data') {
135
+ this._flowing = true;
136
+ installDataEventHandler(this);
137
+ } else if (event == 'readable') {
138
+ installReadableEventHandler(this);
139
+ }
140
+ this._orig_on.call(this, event, listener);
141
+ resetIncomingMessageOverridedMethods(this);
142
+ }
143
+
144
+ function IncomingMessage_emitEndEvent() {
145
+ if (!this._readableState.endEmitted) {
146
+ this._readableState.endEmitted = true;
147
+ this.emit('end');
148
+ }
149
+ }
150
+
151
+ /*
152
+ * Calling on(), pause() etc on the message object may cause our overrided
153
+ * methods to be set to something else. This is probably becaused by the code
154
+ * in Node.js responsible for switching a stream to flowing mode, e.g.
155
+ * emitDataEvents() in _stream_readable.js. Thus, this function
156
+ * should be called from on(), pause() etc.
157
+ */
158
+ function resetIncomingMessageOverridedMethods(message) {
159
+ if (message.pause !== IncomingMessage_pause) {
160
+ message._orig_pause = message.pause;
161
+ message.pause = IncomingMessage_pause;
162
+ }
163
+ if (message.resume !== IncomingMessage_resume) {
164
+ message._orig_resume = message.resume;
165
+ message.resume = IncomingMessage_resume;
166
+ }
167
+ if (message.on !== IncomingMessage_on) {
168
+ message._orig_on = message.on;
169
+ message.on = IncomingMessage_on;
170
+ message.addListener = IncomingMessage_on;
171
+ }
172
+ }
173
+
174
+ function installDataEventHandler(message) {
175
+ if (!message._dataEventHandlerInstalled) {
176
+ message._dataEventHandlerInstalled = true;
177
+ message.socket.on('data', function(chunk) {
178
+ message.emit('data', chunk);
179
+ });
180
+ }
181
+ }
182
+
183
+ function installReadableEventHandler(message) {
184
+ if (!message._readableEventHandlerInstalled) {
185
+ message._readableEventHandlerInstalled = true;
186
+ message.socket.on('readable', function() {
187
+ message.emit('readable');
188
+ });
189
+ }
190
+ }
191
+
124
192
  function createServerResponse(req) {
125
193
  var res = new http.ServerResponse(req);
126
194
  res.assignSocket(req.socket);
@@ -36,12 +36,20 @@ function RequestHandler(readyCallback, clientCallback) {
36
36
  var state = 'PARSING_HEADER';
37
37
  var parser = new SessionProtocolParser();
38
38
 
39
+ function handleReadable() {
40
+ // read(n) returns null unless the buffer is at least n bytes.
41
+ // We just want to read whatever we can we poke into its buffer.
42
+ // Hope they don't change the this.
43
+ var len = socket._readableState.length;
44
+ handleData(socket.read(len));
45
+ }
46
+
39
47
  function handleData(data) {
40
48
  if (state == 'PARSING_HEADER') {
41
49
  var consumed = parser.feed(data);
42
50
  if (parser.state == SessionProtocolParser.SPP_DONE) {
43
51
  state = 'HEADER_SEEN';
44
- socket.removeListener('data', handleData);
52
+ socket.removeListener('readable', handleReadable);
45
53
  PhusionPassenger.emit('request', parser, socket, data.slice(consumed));
46
54
  } else if (parser.state == SessionProtocolParser.SPP_ERROR) {
47
55
  console.error('Header parse error');
@@ -52,7 +60,7 @@ function RequestHandler(readyCallback, clientCallback) {
52
60
  }
53
61
  }
54
62
 
55
- socket.on('data', handleData);
63
+ socket.on('readable', handleReadable);
56
64
  }
57
65
 
58
66
  var server = net.createServer({ allowHalfOpen: true }, handleNewClient);
@@ -0,0 +1,32 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ # This Vagrantfile sets up a CentOS VM, for the purpose of RPM development.
5
+
6
+ ROOT = File.expand_path(File.dirname(__FILE__) + "/..")
7
+
8
+ # Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
9
+ VAGRANTFILE_API_VERSION = "2"
10
+
11
+ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
12
+ config.vm.box = "centos-6.4-x86_64"
13
+ config.vm.box_url = "http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130731.box"
14
+ config.vm.synced_folder ROOT, "/vagrant"
15
+ config.ssh.forward_agent = true
16
+
17
+ config.vm.provider :vmware_fusion do |f, override|
18
+ override.vm.box_url = "https://dl.dropbox.com/u/5721940/vagrant-boxes/vagrant-centos-6.4-x86_64-vmware_fusion.box"
19
+ end
20
+
21
+ pkg_cmd = %Q{
22
+ rpm -Uvh http://mirror.overthewire.com.au/pub/epel/6/i386/epel-release-6-8.noarch.rpm &&
23
+ yum update -y &&
24
+ yum install -y @development-tools fedora-packager git sudo nano &&
25
+ yum install -y gcc gcc-c++ ccache curl-devel openssl-devel zlib-devel &&
26
+ yum install -y ruby ruby-devel rubygems rubygem-rake httpd httpd-devel apr-devel apr-util-devel &&
27
+ usermod -a -G mock vagrant &&
28
+ sed -i 's/Defaults requiretty//' /etc/sudoers &&
29
+ sudo -u vagrant -H rpmdev-setuptree
30
+ }
31
+ config.vm.provision :shell, :inline => pkg_cmd
32
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python
2
+ vars = { 'config_opts': {} }
3
+ execfile("/etc/mock/default.cfg", vars)
4
+ print(vars['config_opts']['dist'])
@@ -22,10 +22,12 @@ namespace tut {
22
22
  PipeWatcher::onData = PipeWatcher::DataCallback();
23
23
  gatherOutput = boost::bind(&ApplicationPool2_DirectSpawnerTest::_gatherOutput, this, _1, _2);
24
24
  setLogLevel(LVL_ERROR); // TODO: change to LVL_WARN
25
+ setPrintAppOutputAsDebuggingMessages(true);
25
26
  }
26
27
 
27
28
  ~ApplicationPool2_DirectSpawnerTest() {
28
29
  setLogLevel(DEFAULT_LOG_LEVEL);
30
+ setPrintAppOutputAsDebuggingMessages(false);
29
31
  unlink("stub/wsgi/passenger_wsgi.pyc");
30
32
  PipeWatcher::onData = PipeWatcher::DataCallback();
31
33
  }
@@ -63,7 +65,6 @@ namespace tut {
63
65
  options.startTimeout = 300;
64
66
 
65
67
  DirectSpawner spawner(bg.safe, *resourceLocator, generation);
66
- spawner.getConfig()->forwardStderr = false;
67
68
 
68
69
  try {
69
70
  process = spawner.spawn(options);
@@ -86,7 +87,6 @@ namespace tut {
86
87
  options.startupFile = ".";
87
88
 
88
89
  DirectSpawner spawner(bg.safe, *resourceLocator, generation);
89
- spawner.getConfig()->forwardStderr = false;
90
90
 
91
91
  try {
92
92
  process = spawner.spawn(options);
@@ -37,11 +37,12 @@ namespace tut {
37
37
  spawnerConfig = boost::make_shared<SpawnerConfig>();
38
38
  spawnerFactory = boost::make_shared<SpawnerFactory>(bg.safe, *resourceLocator,
39
39
  generation, spawnerConfig);
40
- pool = boost::make_shared<Pool>(bg.safe.get(), spawnerFactory);
40
+ pool = boost::make_shared<Pool>(spawnerFactory);
41
41
  pool->initialize();
42
42
  bg.start();
43
43
  callback = boost::bind(&ApplicationPool2_PoolTest::_callback, this, _1, _2);
44
44
  setLogLevel(LVL_ERROR); // TODO: change to LVL_WARN
45
+ setPrintAppOutputAsDebuggingMessages(true);
45
46
  }
46
47
 
47
48
  ~ApplicationPool2_PoolTest() {
@@ -55,6 +56,7 @@ namespace tut {
55
56
  UPDATE_TRACE_POINT();
56
57
  pool.reset();
57
58
  setLogLevel(DEFAULT_LOG_LEVEL);
59
+ setPrintAppOutputAsDebuggingMessages(false);
58
60
  SystemTime::releaseAll();
59
61
  }
60
62
 
@@ -468,7 +470,7 @@ namespace tut {
468
470
  pool->superGroups.get("test")->groups[0]->getWaitlist.size(), 1u);
469
471
 
470
472
  // Close an existing session so that one process is no
471
- // longer at full capacity.
473
+ // longer at full utilization.
472
474
  sessions[0].reset();
473
475
  ensure_equals("The get request has been removed from the wait list",
474
476
  pool->superGroups.get("test")->groups[0]->getWaitlist.size(), 0u);
@@ -476,11 +478,11 @@ namespace tut {
476
478
  }
477
479
 
478
480
  TEST_METHOD(10) {
479
- // If multiple matching processes exist, and all of them are at full capacity,
481
+ // If multiple matching processes exist, and all of them are at full utilization,
480
482
  // and a new process may be spawned,
481
483
  // then asyncGet() will put the action on the group's wait queue and spawn the
482
484
  // new process.
483
- // The process that first becomes not at full capacity
485
+ // The process that first becomes not at full utilization
484
486
  // or the newly spawned process
485
487
  // will process the action, whichever is earlier.
486
488
  // Here we test the case where an existing process is earlier.
@@ -1316,7 +1318,6 @@ namespace tut {
1316
1318
  options.appRoot = "tmp.wsgi";
1317
1319
  options.appType = "wsgi";
1318
1320
  options.spawnMethod = "direct";
1319
- spawnerConfig->forwardStderr = false;
1320
1321
 
1321
1322
  writeFile("tmp.wsgi/passenger_wsgi.py",
1322
1323
  "import sys\n"
@@ -1342,7 +1343,6 @@ namespace tut {
1342
1343
  options.appType = "wsgi";
1343
1344
  options.spawnMethod = "direct";
1344
1345
  options.minProcesses = 4;
1345
- spawnerConfig->forwardStderr = false;
1346
1346
 
1347
1347
  writeFile("tmp.wsgi/counter", "0");
1348
1348
  chmod("tmp.wsgi/counter", 0666);
@@ -1547,7 +1547,6 @@ namespace tut {
1547
1547
  options.appRoot = "tmp.wsgi";
1548
1548
  options.appType = "wsgi";
1549
1549
  options.spawnMethod = "direct";
1550
- spawnerConfig->forwardStderr = false;
1551
1550
  pool->setMax(1);
1552
1551
 
1553
1552
  writeFile("tmp.wsgi/passenger_wsgi.py",
@@ -1598,7 +1597,6 @@ namespace tut {
1598
1597
  options.appType = "wsgi";
1599
1598
  options.spawnMethod = "direct";
1600
1599
  options.minProcesses = 2;
1601
- spawnerConfig->forwardStderr = false;
1602
1600
 
1603
1601
  // Spawn 2 processes.
1604
1602
  retainSessions = true;
@@ -1722,6 +1720,59 @@ namespace tut {
1722
1720
  debug->debugger->recv("Restarting aborted");
1723
1721
  }
1724
1722
 
1723
+ TEST_METHOD(79) {
1724
+ // Test sticky sessions.
1725
+
1726
+ // Spawn 2 processes and get their sticky session IDs and PIDs.
1727
+ ensureMinProcesses(2);
1728
+ Options options = createOptions();
1729
+ SessionPtr session1 = pool->get(options, &ticket);
1730
+ SessionPtr session2 = pool->get(options, &ticket);
1731
+ int id1 = session1->getStickySessionId();
1732
+ int id2 = session2->getStickySessionId();
1733
+ pid_t pid1 = session1->getPid();
1734
+ pid_t pid2 = session2->getPid();
1735
+ session1.reset();
1736
+ session2.reset();
1737
+
1738
+ // Make two requests with id1 as sticky session ID. They should
1739
+ // both go to process pid1.
1740
+ options.stickySessionId = id1;
1741
+ session1 = pool->get(options, &ticket);
1742
+ ensure_equals("Request 1.1 goes to process 1", session1->getPid(), pid1);
1743
+ // The second request should be queued, and should not finish until
1744
+ // the first request is finished.
1745
+ ensure_equals(number, 1);
1746
+ pool->asyncGet(options, callback);
1747
+ SHOULD_NEVER_HAPPEN(100,
1748
+ result = number > 1;
1749
+ );
1750
+ session1.reset();
1751
+ EVENTUALLY(1,
1752
+ result = number == 2;
1753
+ );
1754
+ ensure_equals("Request 1.2 goes to process 1", currentSession->getPid(), pid1);
1755
+ currentSession.reset();
1756
+
1757
+ // Make two requests with id2 as sticky session ID. They should
1758
+ // both go to process pid2.
1759
+ options.stickySessionId = id2;
1760
+ session1 = pool->get(options, &ticket);
1761
+ ensure_equals("Request 2.1 goes to process 2", session1->getPid(), pid2);
1762
+ // The second request should be queued, and should not finish until
1763
+ // the first request is finished.
1764
+ pool->asyncGet(options, callback);
1765
+ SHOULD_NEVER_HAPPEN(100,
1766
+ result = number > 2;
1767
+ );
1768
+ session1.reset();
1769
+ EVENTUALLY(1,
1770
+ result = number == 3;
1771
+ );
1772
+ ensure_equals("Request 2.2 goes to process 2", currentSession->getPid(), pid2);
1773
+ currentSession.reset();
1774
+ }
1775
+
1725
1776
  // TODO: Persistent connections.
1726
1777
  // TODO: If one closes the session before it has reached EOF, and process's maximum concurrency
1727
1778
  // has already been reached, then the pool should ping the process so that it can detect
@@ -1730,7 +1781,7 @@ namespace tut {
1730
1781
 
1731
1782
  /*********** Test previously discovered bugs ***********/
1732
1783
 
1733
- TEST_METHOD(79) {
1784
+ TEST_METHOD(85) {
1734
1785
  // Test detaching, then restarting. This should not violate any invariants.
1735
1786
  TempDirCopy dir("stub/wsgi", "tmp.wsgi");
1736
1787
  Options options = createOptions();
@@ -27,10 +27,12 @@ namespace tut {
27
27
  PipeWatcher::onData = PipeWatcher::DataCallback();
28
28
  gatherOutput = boost::bind(&ApplicationPool2_SmartSpawnerTest::_gatherOutput, this, _1, _2);
29
29
  setLogLevel(LVL_ERROR); // TODO: should be LVL_WARN
30
+ setPrintAppOutputAsDebuggingMessages(true);
30
31
  }
31
32
 
32
33
  ~ApplicationPool2_SmartSpawnerTest() {
33
34
  setLogLevel(DEFAULT_LOG_LEVEL);
35
+ setPrintAppOutputAsDebuggingMessages(false);
34
36
  unlink("stub/wsgi/passenger_wsgi.pyc");
35
37
  PipeWatcher::onData = PipeWatcher::DataCallback();
36
38
  }
@@ -128,8 +130,6 @@ namespace tut {
128
130
  generation,
129
131
  preloaderCommand,
130
132
  options);
131
- spawner.getConfig()->forwardStdout = false;
132
- spawner.getConfig()->forwardStderr = false;
133
133
 
134
134
  try {
135
135
  process = spawner.spawn(options);
@@ -160,8 +160,6 @@ namespace tut {
160
160
  generation,
161
161
  preloaderCommand,
162
162
  options);
163
- spawner.getConfig()->forwardStdout = false;
164
- spawner.getConfig()->forwardStderr = false;
165
163
 
166
164
  try {
167
165
  process = spawner.spawn(options);
@@ -192,8 +190,6 @@ namespace tut {
192
190
  generation,
193
191
  preloaderCommand,
194
192
  options);
195
- spawner.getConfig()->forwardStdout = false;
196
- spawner.getConfig()->forwardStderr = false;
197
193
 
198
194
  try {
199
195
  process = spawner.spawn(options);
@@ -393,10 +393,10 @@ namespace tut {
393
393
  stat.stat("test4.txt", &buf, 1);
394
394
  stat.stat("test5.txt", &buf, 1);
395
395
  stat.setMaxSize(2);
396
- ensure(!stat.knows("test.txt"));
397
- ensure(!stat.knows("test2.txt"));
398
- ensure(!stat.knows("test3.txt"));
399
- ensure(stat.knows("test4.txt"));
400
- ensure(stat.knows("test5.txt"));
396
+ ensure("(1)", !stat.knows("test.txt"));
397
+ ensure("(2)", !stat.knows("test2.txt"));
398
+ ensure("(3)", !stat.knows("test3.txt"));
399
+ ensure("(4)", stat.knows("test4.txt"));
400
+ ensure("(5)", stat.knows("test5.txt"));
401
401
  }
402
402
  }
@@ -42,12 +42,13 @@ namespace tut {
42
42
  RequestHandlerTest() {
43
43
  createServerInstanceDirAndGeneration(serverInstanceDir, generation);
44
44
  spawnerFactory = boost::make_shared<SpawnerFactory>(bg.safe, *resourceLocator, generation);
45
- pool = boost::make_shared<Pool>(bg.safe.get(), spawnerFactory);
45
+ pool = boost::make_shared<Pool>(spawnerFactory);
46
46
  pool->initialize();
47
47
  serverFilename = generation->getPath() + "/server";
48
48
  requestSocket = createUnixServer(serverFilename);
49
49
  setNonBlocking(requestSocket);
50
50
  setLogLevel(LVL_ERROR); // TODO: set to LVL_WARN
51
+ setPrintAppOutputAsDebuggingMessages(true);
51
52
 
52
53
  agentOptions.passengerRoot = resourceLocator->getRoot();
53
54
  agentOptions.defaultUser = testConfig["default_user"].asString();
@@ -63,6 +64,7 @@ namespace tut {
63
64
 
64
65
  ~RequestHandlerTest() {
65
66
  setLogLevel(DEFAULT_LOG_LEVEL);
67
+ setPrintAppOutputAsDebuggingMessages(false);
66
68
  if (bg.isStarted()) {
67
69
  bg.safe->runSync(boost::bind(&RequestHandlerTest::destroy, this));
68
70
  } else {
@@ -332,7 +334,6 @@ namespace tut {
332
334
  "STDERR.puts 'I have failed'");
333
335
 
334
336
  setLogLevel(-2);
335
- spawnerFactory->getConfig()->forwardStderr = false;
336
337
  init();
337
338
  connect();
338
339
  sendHeaders(defaultHeaders,
@@ -357,7 +358,6 @@ namespace tut {
357
358
  "STDERR.puts 'I have failed'\n");
358
359
 
359
360
  setLogLevel(-2);
360
- spawnerFactory->getConfig()->forwardStderr = false;
361
361
  init();
362
362
  connect();
363
363
  sendHeaders(defaultHeaders,
@@ -380,7 +380,6 @@ namespace tut {
380
380
  writeFile("tmp.handler/start.rb", "");
381
381
 
382
382
  setLogLevel(-2);
383
- spawnerFactory->getConfig()->forwardStderr = false;
384
383
  init();
385
384
  connect();
386
385
  sendHeaders(defaultHeaders,
@@ -408,7 +407,6 @@ namespace tut {
408
407
  "STDERR.puts 'I have failed'");
409
408
 
410
409
  setLogLevel(-2);
411
- spawnerFactory->getConfig()->forwardStderr = false;
412
410
  init();
413
411
  connect();
414
412
  sendHeaders(defaultHeaders,
@@ -434,7 +432,6 @@ namespace tut {
434
432
  "STDERR.puts 'I have failed'\n");
435
433
 
436
434
  setLogLevel(-2);
437
- spawnerFactory->getConfig()->forwardStderr = false;
438
435
  init();
439
436
  connect();
440
437
  sendHeaders(defaultHeaders,