passenger 4.0.0.rc4 → 4.0.0.rc6

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 (83) hide show
  1. data.tar.gz.asc +12 -0
  2. data/.travis.yml +4 -4
  3. data/NEWS +46 -0
  4. data/bin/passenger-config +31 -1
  5. data/bin/passenger-install-apache2-module +1 -1
  6. data/bin/passenger-install-nginx-module +1 -0
  7. data/build/common_library.rb +4 -0
  8. data/build/cplusplus_support.rb +27 -6
  9. data/build/cxx_tests.rb +1 -1
  10. data/build/misc.rb +28 -6
  11. data/build/packaging.rb +72 -65
  12. data/build/test_basics.rb +1 -1
  13. data/dev/googlecode_upload.py +265 -0
  14. data/dev/run_travis.sh +9 -0
  15. data/doc/Users guide Apache.html +376 -193
  16. data/doc/Users guide Apache.idmap.txt +80 -62
  17. data/doc/Users guide Apache.txt +61 -35
  18. data/doc/Users guide Nginx.html +278 -83
  19. data/doc/Users guide Nginx.idmap.txt +26 -10
  20. data/doc/Users guide Nginx.txt +59 -31
  21. data/doc/Users guide Standalone.html +1 -1
  22. data/doc/users_guide_snippets/installation.txt +121 -11
  23. data/doc/users_guide_snippets/rvm_helper_tool.txt +56 -0
  24. data/ext/apache2/Bucket.cpp +1 -1
  25. data/ext/apache2/Configuration.cpp +7 -1
  26. data/ext/apache2/Configuration.hpp +4 -0
  27. data/ext/apache2/Hooks.cpp +2 -2
  28. data/ext/common/AgentsStarter.cpp +2 -2
  29. data/ext/common/AgentsStarter.h +1 -1
  30. data/ext/common/AgentsStarter.hpp +2 -2
  31. data/ext/common/ApplicationPool2/DirectSpawner.h +4 -8
  32. data/ext/common/ApplicationPool2/Group.h +17 -11
  33. data/ext/common/ApplicationPool2/Implementation.cpp +39 -11
  34. data/ext/common/ApplicationPool2/Pool.h +23 -4
  35. data/ext/common/ApplicationPool2/Process.h +30 -11
  36. data/ext/common/ApplicationPool2/SmartSpawner.h +3 -1
  37. data/ext/common/Constants.h +1 -1
  38. data/ext/common/EventedBufferedInput.h +4 -0
  39. data/ext/common/Utils.cpp +21 -3
  40. data/ext/common/Utils.h +8 -1
  41. data/ext/common/Utils/HttpHeaderBufferer.h +1 -1
  42. data/ext/common/Utils/IOUtils.cpp +5 -4
  43. data/ext/common/Utils/IOUtils.h +32 -14
  44. data/ext/common/Utils/MessagePassing.h +2 -2
  45. data/ext/common/Utils/ProcessMetricsCollector.h +47 -15
  46. data/ext/common/Utils/ScopeGuard.h +20 -3
  47. data/ext/common/Utils/StrIntUtils.h +14 -5
  48. data/ext/common/agents/Base.cpp +161 -50
  49. data/ext/common/agents/HelperAgent/AgentOptions.h +2 -2
  50. data/ext/common/agents/HelperAgent/Main.cpp +1 -0
  51. data/ext/common/agents/HelperAgent/RequestHandler.h +166 -52
  52. data/ext/common/agents/LoggingAgent/Main.cpp +1 -1
  53. data/ext/common/agents/Watchdog/Main.cpp +2 -2
  54. data/ext/nginx/Configuration.c +31 -4
  55. data/ext/nginx/Configuration.h +1 -0
  56. data/ext/nginx/ContentHandler.c +148 -34
  57. data/ext/nginx/ngx_http_passenger_module.c +4 -1
  58. data/ext/oxt/detail/spin_lock_pthreads.hpp +4 -4
  59. data/ext/oxt/macros.hpp +30 -8
  60. data/lib/phusion_passenger.rb +2 -2
  61. data/lib/phusion_passenger/classic_rails/thread_handler_extension.rb +1 -1
  62. data/lib/phusion_passenger/native_support.rb +19 -1
  63. data/lib/phusion_passenger/platform_info/compiler.rb +6 -0
  64. data/lib/phusion_passenger/platform_info/ruby.rb +54 -5
  65. data/lib/phusion_passenger/preloader_shared_helpers.rb +8 -1
  66. data/lib/phusion_passenger/rack/out_of_band_gc.rb +3 -1
  67. data/lib/phusion_passenger/rack/thread_handler_extension.rb +32 -5
  68. data/lib/phusion_passenger/request_handler/thread_handler.rb +28 -8
  69. data/lib/phusion_passenger/ruby_core_enhancements.rb +9 -1
  70. data/lib/phusion_passenger/standalone/runtime_installer.rb +1 -0
  71. data/lib/phusion_passenger/utils/unseekable_socket.rb +50 -5
  72. data/passenger.gemspec +1 -1
  73. data/resources/templates/apache2/config_snippets.txt.erb +1 -1
  74. data/test/cxx/ApplicationPool2/PoolTest.cpp +4 -9
  75. data/test/cxx/RequestHandlerTest.cpp +5 -5
  76. data/test/ruby/classic_rails/loader_spec.rb +1 -1
  77. data/test/ruby/classic_rails/preloader_spec.rb +1 -1
  78. data/test/ruby/request_handler_spec.rb +207 -1
  79. data/test/ruby/shared/loader_sharedspec.rb +1 -0
  80. data/test/ruby/spec_helper.rb +11 -1
  81. data/test/stub/apache2/httpd.conf.erb +1 -1
  82. metadata +5 -3
  83. metadata.gz.asc +12 -0
@@ -42,7 +42,7 @@ struct AgentOptions {
42
42
  string defaultUser;
43
43
  string defaultGroup;
44
44
  string passengerRoot;
45
- string rubyCommand;
45
+ string defaultRubyCommand;
46
46
  unsigned int generationNumber;
47
47
  unsigned int maxPoolSize;
48
48
  unsigned int maxInstancesPerApp;
@@ -62,7 +62,7 @@ struct AgentOptions {
62
62
  passengerRoot = options.get("passenger_root");
63
63
  tempDir = options.get("temp_dir");
64
64
  userSwitching = options.getBool("user_switching");
65
- rubyCommand = options.get("ruby");
65
+ defaultRubyCommand = options.get("default_ruby");
66
66
  defaultUser = options.get("default_user");
67
67
  defaultGroup = options.get("default_group");
68
68
  maxPoolSize = options.getInt("max_pool_size");
@@ -435,6 +435,7 @@ public:
435
435
 
436
436
  function<void ()> func = boost::bind(prestartWebApps,
437
437
  resourceLocator,
438
+ options.defaultRubyCommand,
438
439
  options.prestartUrls
439
440
  );
440
441
  prestarterThread = ptr(new oxt::thread(
@@ -535,6 +535,15 @@ typedef shared_ptr<Client> ClientPtr;
535
535
 
536
536
 
537
537
  class RequestHandler {
538
+ public:
539
+ enum BenchmarkPoint {
540
+ BP_NONE,
541
+ BP_AFTER_ACCEPT,
542
+ BP_AFTER_CHECK_CONNECT_PASSWORD,
543
+ BP_AFTER_PARSING_HEADER,
544
+ BP_BEFORE_CHECKOUT_SESSION
545
+ };
546
+
538
547
  private:
539
548
  friend class Client;
540
549
  typedef UnionStation::LoggerFactory LoggerFactory;
@@ -597,6 +606,17 @@ private:
597
606
  disconnect(client);
598
607
  }
599
608
 
609
+ template<typename Number>
610
+ static Number clamp(Number n, Number min, Number max) {
611
+ if (n < min) {
612
+ return min;
613
+ } else if (n > max) {
614
+ return max;
615
+ } else {
616
+ return n;
617
+ }
618
+ }
619
+
600
620
  // GDB helper function, implemented in .cpp file to prevent inlining.
601
621
  Client *getClientPointer(const ClientPtr &client);
602
622
 
@@ -613,7 +633,7 @@ private:
613
633
  }
614
634
  }
615
635
 
616
- static long long getLongLongOption(const ClientPtr &client, const StaticString &name, long long defaultValue = -1) {
636
+ static long long getULongLongOption(const ClientPtr &client, const StaticString &name, long long defaultValue = -1) {
617
637
  ScgiRequestParser::const_iterator it = client->scgiParser.getHeaderIterator(name);
618
638
  if (it != client->scgiParser.end()) {
619
639
  long long result = stringToULL(it->second);
@@ -628,6 +648,31 @@ private:
628
648
  }
629
649
  }
630
650
 
651
+ void writeSimpleResponse(const ClientPtr &client, const StaticString &data) {
652
+ char header[256];
653
+ char *pos = header;
654
+ const char *end = header + sizeof(header) - 1;
655
+
656
+ if (getBoolOption(client, "PASSENGER_STATUS_LINE", true)) {
657
+ pos = appendData(pos, end, "HTTP/1.1 200 OK\r\n");
658
+ }
659
+ pos += snprintf(pos, end - pos,
660
+ "Status: 200 OK\r\n"
661
+ "Content-Length: %lu\r\n"
662
+ "Content-Type: text/html; charset=UTF-8\r\n"
663
+ "Cache-Control: no-cache, no-store, must-revalidate\r\n"
664
+ "\r\n",
665
+ (unsigned long) data.size());
666
+
667
+ client->clientOutputPipe->write(header, pos - header);
668
+ client->clientOutputPipe->write(data.data(), data.size());
669
+ client->clientOutputPipe->end();
670
+
671
+ if (client->useUnionStation()) {
672
+ client->logMessage("Status: 200 OK");
673
+ }
674
+ }
675
+
631
676
  void writeErrorResponse(const ClientPtr &client, const StaticString &message, const SpawnException *e = NULL) {
632
677
  assert(client->state < Client::FORWARDING_BODY_TO_APP);
633
678
  client->state = Client::WRITING_SIMPLE_RESPONSE;
@@ -636,44 +681,56 @@ private:
636
681
  string data;
637
682
 
638
683
  if (getBoolOption(client, "PASSENGER_FRIENDLY_ERROR_PAGES", true)) {
639
- string cssFile = templatesDir + "/error_layout.css";
640
- string errorLayoutFile = templatesDir + "/error_layout.html.template";
641
- string generalErrorFile =
642
- (e != NULL && e->isHTML())
643
- ? templatesDir + "/general_error_with_html.html.template"
644
- : templatesDir + "/general_error.html.template";
645
- string css = readAll(cssFile);
646
- StringMap<StaticString> params;
647
-
648
- params.set("CSS", css);
649
- params.set("APP_ROOT", client->options.appRoot);
650
- params.set("RUBY", client->options.ruby);
651
- params.set("ENVIRONMENT", client->options.environment);
652
- params.set("MESSAGE", message);
653
- params.set("IS_RUBY_APP",
654
- (client->options.appType == "classic-rails" || client->options.appType == "rack")
655
- ? "true" : "false");
656
- if (e != NULL) {
657
- params.set("TITLE", "Web application could not be started");
658
- // Store all SpawnException annotations into 'params',
659
- // but convert its name to uppercase.
660
- const map<string, string> &annotations = e->getAnnotations();
661
- map<string, string>::const_iterator it, end = annotations.end();
662
- for (it = annotations.begin(); it != end; it++) {
663
- string name = it->first;
664
- for (string::size_type i = 0; i < name.size(); i++) {
665
- name[i] = toupper(name[i]);
684
+ try {
685
+ string cssFile = templatesDir + "/error_layout.css";
686
+ string errorLayoutFile = templatesDir + "/error_layout.html.template";
687
+ string generalErrorFile =
688
+ (e != NULL && e->isHTML())
689
+ ? templatesDir + "/general_error_with_html.html.template"
690
+ : templatesDir + "/general_error.html.template";
691
+ string css = readAll(cssFile);
692
+ StringMap<StaticString> params;
693
+
694
+ params.set("CSS", css);
695
+ params.set("APP_ROOT", client->options.appRoot);
696
+ params.set("RUBY", client->options.ruby);
697
+ params.set("ENVIRONMENT", client->options.environment);
698
+ params.set("MESSAGE", message);
699
+ params.set("IS_RUBY_APP",
700
+ (client->options.appType == "classic-rails" || client->options.appType == "rack")
701
+ ? "true" : "false");
702
+ if (e != NULL) {
703
+ params.set("TITLE", "Web application could not be started");
704
+ // Store all SpawnException annotations into 'params',
705
+ // but convert its name to uppercase.
706
+ const map<string, string> &annotations = e->getAnnotations();
707
+ map<string, string>::const_iterator it, end = annotations.end();
708
+ for (it = annotations.begin(); it != end; it++) {
709
+ string name = it->first;
710
+ for (string::size_type i = 0; i < name.size(); i++) {
711
+ name[i] = toupper(name[i]);
712
+ }
713
+ params.set(name, it->second);
666
714
  }
667
- params.set(name, it->second);
715
+ } else {
716
+ params.set("TITLE", "Internal server error");
668
717
  }
669
- } else {
670
- params.set("TITLE", "Internal server error");
718
+ string content = Template::apply(readAll(generalErrorFile), params);
719
+ params.set("CONTENT", content);
720
+ data = Template::apply(readAll(errorLayoutFile), params);
721
+ } catch (const SystemException &e2) {
722
+ P_ERROR("Cannot render an error page: " << e2.what() << "\n" <<
723
+ e2.backtrace());
724
+ data = message;
671
725
  }
672
- string content = Template::apply(readAll(generalErrorFile), params);
673
- params.set("CONTENT", content);
674
- data = Template::apply(readAll(errorLayoutFile), params);
675
726
  } else {
676
- data = readAll(templatesDir + "/undisclosed_error.html.template");
727
+ try {
728
+ data = readAll(templatesDir + "/undisclosed_error.html.template");
729
+ } catch (const SystemException &e2) {
730
+ P_ERROR("Cannot render an error page: " << e2.what() << "\n" <<
731
+ e2.backtrace());
732
+ data = "Internal Server Error";
733
+ }
677
734
  }
678
735
 
679
736
  stringstream str;
@@ -686,7 +743,7 @@ private:
686
743
  str << "Cache-Control: no-cache, no-store, must-revalidate\r\n";
687
744
  str << "\r\n";
688
745
 
689
- const string &header = str.str();
746
+ const string header = str.str();
690
747
  client->clientOutputPipe->write(header.data(), header.size());
691
748
  client->clientOutputPipe->write(data.data(), data.size());
692
749
  client->clientOutputPipe->end();
@@ -697,6 +754,24 @@ private:
697
754
  }
698
755
  }
699
756
 
757
+ static BenchmarkPoint getDefaultBenchmarkPoint() {
758
+ const char *val = getenv("PASSENGER_REQUEST_HANDLER_BENCHMARK_POINT");
759
+ if (val == NULL || *val == '\0') {
760
+ return BP_NONE;
761
+ } else if (strcmp(val, "after_accept") == 0) {
762
+ return BP_AFTER_ACCEPT;
763
+ } else if (strcmp(val, "after_check_connect_password") == 0) {
764
+ return BP_AFTER_CHECK_CONNECT_PASSWORD;
765
+ } else if (strcmp(val, "after_parsing_header") == 0) {
766
+ return BP_AFTER_PARSING_HEADER;
767
+ } else if (strcmp(val, "before_checkout_session") == 0) {
768
+ return BP_BEFORE_CHECKOUT_SESSION;
769
+ } else {
770
+ P_WARN("Invalid RequestHandler benchmark point requested: " << val);
771
+ return BP_NONE;
772
+ }
773
+ }
774
+
700
775
 
701
776
  /*****************************************************
702
777
  * COMPONENT: appInput -> clientOutputPipe plumbing
@@ -792,10 +867,16 @@ private:
792
867
  }
793
868
 
794
869
  bool addStatusHeaderFromStatusLine(const ClientPtr &client, string &headerData) {
795
- string::size_type begin = headerData.find(' ');
796
- string::size_type end = headerData.find("\r\n");
870
+ string::size_type begin, end;
871
+
872
+ begin = headerData.find(' ');
873
+ if (begin != string::npos) {
874
+ end = headerData.find("\r\n", begin + 1);
875
+ } else {
876
+ end = string::npos;
877
+ }
797
878
  if (begin != string::npos && end != string::npos) {
798
- StaticString statusValue(headerData.data() + begin, end - begin);
879
+ StaticString statusValue(headerData.data() + begin + 1, end - begin);
799
880
  char header[statusValue.size() + 20];
800
881
  char *pos = header;
801
882
  const char *end = header + statusValue.size() + 20;
@@ -803,7 +884,7 @@ private:
803
884
  pos = appendData(pos, end, "Status: ");
804
885
  pos = appendData(pos, end, statusValue);
805
886
  pos = appendData(pos, end, "\r\n");
806
- headerData.append(header);
887
+ headerData.append(StaticString(header, pos - header));
807
888
  return true;
808
889
  } else {
809
890
  disconnectWithError(client, "application sent malformed response: the HTTP status line is invalid.");
@@ -870,7 +951,7 @@ private:
870
951
  const StaticString &origHeaderData)
871
952
  {
872
953
  string headerData;
873
- headerData.reserve(origHeaderData.size() + 100);
954
+ headerData.reserve(origHeaderData.size() + 150);
874
955
  // Strip trailing CRLF.
875
956
  headerData.append(origHeaderData.data(), origHeaderData.size() - 2);
876
957
 
@@ -1181,8 +1262,10 @@ private:
1181
1262
  void onAcceptable(ev::io &io, int revents) {
1182
1263
  bool endReached = false;
1183
1264
  unsigned int count = 0;
1265
+ unsigned int maxAcceptTries = clamp<unsigned int>(clients.size(), 1, 10);
1266
+ ClientPtr acceptedClients[10];
1184
1267
 
1185
- while (!endReached && count < 10) {
1268
+ while (!endReached && count < maxAcceptTries) {
1186
1269
  FileDescriptor fd = acceptNonBlockingSocket(requestSocket);
1187
1270
  if (fd == -1) {
1188
1271
  if (errno == EAGAIN || errno == EWOULDBLOCK) {
@@ -1191,20 +1274,34 @@ private:
1191
1274
  int e = errno;
1192
1275
  P_ERROR("Cannot accept client: " << strerror(e) <<
1193
1276
  " (errno=" << e << "). " <<
1194
- "Pausing listening on server socket for 3 seconds.");
1277
+ "Pausing listening on server socket for 3 seconds. " <<
1278
+ "Current client count: " << clients.size());
1195
1279
  requestSocketWatcher.stop();
1196
1280
  resumeSocketWatcherTimer.start();
1197
1281
  endReached = true;
1198
1282
  }
1283
+ } else if (benchmarkPoint == BP_AFTER_ACCEPT) {
1284
+ writeExact(fd,
1285
+ "HTTP/1.1 200 OK\r\n"
1286
+ "Status: 200 OK\r\n"
1287
+ "Content-Type: text/html\r\n"
1288
+ "Connection: close\r\n"
1289
+ "\r\n"
1290
+ "Benchmark point: after_accept\n");
1199
1291
  } else {
1200
1292
  ClientPtr client = make_shared<Client>();
1201
1293
  client->associate(this, fd);
1202
1294
  clients.insert(make_pair<int, ClientPtr>(fd, client));
1295
+ acceptedClients[count] = client;
1203
1296
  count++;
1204
1297
  RH_DEBUG(client, "New client accepted; new client count = " << clients.size());
1205
1298
  }
1206
1299
  }
1207
1300
 
1301
+ for (unsigned int i = 0; i < count; i++) {
1302
+ acceptedClients[i]->clientInput->readNow();
1303
+ }
1304
+
1208
1305
  if (OXT_LIKELY(!clients.empty())) {
1209
1306
  inactivityTimer.stop();
1210
1307
  }
@@ -1286,6 +1383,7 @@ private:
1286
1383
  if (errnoCode == ECONNRESET) {
1287
1384
  // We might as well treat ECONNRESET like an EOF.
1288
1385
  // http://stackoverflow.com/questions/2974021/what-does-econnreset-mean-in-the-context-of-an-af-local-socket
1386
+ RH_TRACE(client, 3, "Client socket ECONNRESET error; treating it as EOF");
1289
1387
  onClientEof(client);
1290
1388
  } else {
1291
1389
  stringstream message;
@@ -1410,6 +1508,10 @@ private:
1410
1508
  client->state = Client::READING_HEADER;
1411
1509
  client->freeBufferedConnectPassword();
1412
1510
  client->timeoutTimer.stop();
1511
+
1512
+ if (benchmarkPoint == BP_AFTER_CHECK_CONNECT_PASSWORD) {
1513
+ writeSimpleResponse(client, "Benchmark point: after_check_connect_password\n");
1514
+ }
1413
1515
  } else {
1414
1516
  disconnectWithError(client, "wrong connect password");
1415
1517
  }
@@ -1623,6 +1725,11 @@ private:
1623
1725
  return consumed;
1624
1726
  }
1625
1727
 
1728
+ if (benchmarkPoint == BP_AFTER_PARSING_HEADER) {
1729
+ writeSimpleResponse(client, "Benchmark point: after_parsing_header\n");
1730
+ return consumed;
1731
+ }
1732
+
1626
1733
  bool modified = modifyClientHeaders(client);
1627
1734
  /* TODO: in case the headers are not modified, we only need to rebuild the header data
1628
1735
  * right now because the scgiParser buffer is invalidated as soon as onClientData exits.
@@ -1630,7 +1737,7 @@ private:
1630
1737
  * onClientData exits.
1631
1738
  */
1632
1739
  parser.rebuildData(modified);
1633
- client->contentLength = getLongLongOption(client, "CONTENT_LENGTH");
1740
+ client->contentLength = getULongLongOption(client, "CONTENT_LENGTH");
1634
1741
  fillPoolOptions(client);
1635
1742
  if (!client->connected()) {
1636
1743
  return consumed;
@@ -1735,13 +1842,17 @@ private:
1735
1842
  }
1736
1843
 
1737
1844
  void checkoutSession(const ClientPtr &client) {
1738
- RH_TRACE(client, 2, "Checking out session: appRoot=" << client->options.appRoot);
1739
- client->state = Client::CHECKING_OUT_SESSION;
1740
- client->beginScopeLog(&client->scopeLogs.getFromPool, "get from pool");
1741
- pool->asyncGet(client->options, boost::bind(&RequestHandler::sessionCheckedOut,
1742
- this, client, _1, _2));
1743
- if (!client->sessionCheckedOut) {
1744
- client->backgroundOperations++;
1845
+ if (benchmarkPoint != BP_BEFORE_CHECKOUT_SESSION) {
1846
+ RH_TRACE(client, 2, "Checking out session: appRoot=" << client->options.appRoot);
1847
+ client->state = Client::CHECKING_OUT_SESSION;
1848
+ client->beginScopeLog(&client->scopeLogs.getFromPool, "get from pool");
1849
+ pool->asyncGet(client->options, boost::bind(&RequestHandler::sessionCheckedOut,
1850
+ this, client, _1, _2));
1851
+ if (!client->sessionCheckedOut) {
1852
+ client->backgroundOperations++;
1853
+ }
1854
+ } else {
1855
+ writeSimpleResponse(client, "Benchmark point: before_checkout_session\n");
1745
1856
  }
1746
1857
  }
1747
1858
 
@@ -2125,6 +2236,8 @@ public:
2125
2236
  // For unit testing purposes.
2126
2237
  unsigned int connectPasswordTimeout; // milliseconds
2127
2238
 
2239
+ BenchmarkPoint benchmarkPoint;
2240
+
2128
2241
  RequestHandler(const SafeLibevPtr &_libev,
2129
2242
  const FileDescriptor &_requestSocket,
2130
2243
  const PoolPtr &_pool,
@@ -2133,7 +2246,8 @@ public:
2133
2246
  requestSocket(_requestSocket),
2134
2247
  pool(_pool),
2135
2248
  options(_options),
2136
- resourceLocator(_options.passengerRoot)
2249
+ resourceLocator(_options.passengerRoot),
2250
+ benchmarkPoint(getDefaultBenchmarkPoint())
2137
2251
  {
2138
2252
  accept4Available = true;
2139
2253
  connectPasswordTimeout = 15000;
@@ -269,7 +269,7 @@ main(int argc, char *argv[]) {
269
269
 
270
270
  /********** Initialized! Enter main loop... **********/
271
271
 
272
- P_WARN("Logging agent online, listening at " << socketAddress);
272
+ P_WARN("PassengerLoggingAgent online, listening at " << socketAddress);
273
273
  ev_run(eventLoop, 0);
274
274
  P_DEBUG("Logging agent exiting with code " << exitCode << ".");
275
275
  return exitCode;
@@ -1074,8 +1074,8 @@ main(int argc, char *argv[]) {
1074
1074
  .setDefault ("default_user", DEFAULT_WEB_APP_USER)
1075
1075
  .setDefaultUid ("web_server_worker_uid", getuid())
1076
1076
  .setDefaultGid ("web_server_worker_gid", getgid())
1077
- .setDefault ("ruby", DEFAULT_RUBY)
1078
- .setDefault ("python", DEFAULT_PYTHON)
1077
+ .setDefault ("default_ruby", DEFAULT_RUBY)
1078
+ .setDefault ("default_python", DEFAULT_PYTHON)
1079
1079
  .setDefaultInt ("max_pool_size", DEFAULT_MAX_POOL_SIZE)
1080
1080
  .setDefaultInt ("max_instances_per_app", DEFAULT_MAX_INSTANCES_PER_APP)
1081
1081
  .setDefaultInt ("pool_idle_time", DEFAULT_POOL_IDLE_TIME);
@@ -67,7 +67,9 @@ passenger_create_main_conf(ngx_conf_t *cf)
67
67
  return NGX_CONF_ERROR;
68
68
  }
69
69
 
70
- conf->log_level = (ngx_int_t) NGX_CONF_UNSET;
70
+ conf->default_ruby.data = NULL;
71
+ conf->default_ruby.len = 0;
72
+ conf->log_level = (ngx_int_t) NGX_CONF_UNSET;
71
73
  conf->debug_log_file.data = NULL;
72
74
  conf->debug_log_file.len = 0;
73
75
  conf->abort_on_startup_error = NGX_CONF_UNSET;
@@ -110,6 +112,11 @@ passenger_init_main_conf(ngx_conf_t *cf, void *conf_pointer)
110
112
  conf = &passenger_main_conf;
111
113
  *conf = *((passenger_main_conf_t *) conf_pointer);
112
114
 
115
+ if (conf->default_ruby.len == 0) {
116
+ conf->default_ruby.data = (u_char *) DEFAULT_RUBY;
117
+ conf->default_ruby.len = strlen(DEFAULT_RUBY);
118
+ }
119
+
113
120
  if (conf->log_level == (ngx_int_t) NGX_CONF_UNSET) {
114
121
  conf->log_level = DEFAULT_LOG_LEVEL;
115
122
  }
@@ -272,6 +279,8 @@ passenger_create_loc_conf(ngx_conf_t *cf)
272
279
  conf->upstream_config.buffering = NGX_CONF_UNSET;
273
280
  conf->upstream_config.ignore_client_abort = NGX_CONF_UNSET;
274
281
 
282
+ conf->upstream_config.local = NGX_CONF_UNSET_PTR;
283
+
275
284
  conf->upstream_config.connect_timeout = NGX_CONF_UNSET_MSEC;
276
285
  conf->upstream_config.send_timeout = NGX_CONF_UNSET_MSEC;
277
286
  conf->upstream_config.read_timeout = NGX_CONF_UNSET_MSEC;
@@ -292,6 +301,10 @@ passenger_create_loc_conf(ngx_conf_t *cf)
292
301
  conf->upstream_config.cache_bypass = NGX_CONF_UNSET_PTR;
293
302
  conf->upstream_config.no_cache = NGX_CONF_UNSET_PTR;
294
303
  conf->upstream_config.cache_valid = NGX_CONF_UNSET_PTR;
304
+ #if NGINX_VERSION_NUM >= 1002000
305
+ conf->upstream_config.cache_lock = NGX_CONF_UNSET;
306
+ conf->upstream_config.cache_lock_timeout = NGX_CONF_UNSET_MSEC;
307
+ #endif
295
308
  #endif
296
309
 
297
310
  conf->upstream_config.intercept_errors = NGX_CONF_UNSET;
@@ -420,10 +433,13 @@ passenger_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
420
433
  prev->upstream_config.store_access, 0600);
421
434
 
422
435
  ngx_conf_merge_value(conf->upstream_config.buffering,
423
- prev->upstream_config.buffering, 0);
436
+ prev->upstream_config.buffering, 0);
424
437
 
425
438
  ngx_conf_merge_value(conf->upstream_config.ignore_client_abort,
426
- prev->upstream_config.ignore_client_abort, 0);
439
+ prev->upstream_config.ignore_client_abort, 0);
440
+
441
+ ngx_conf_merge_ptr_value(conf->upstream_config.local,
442
+ prev->upstream_config.local, NULL);
427
443
 
428
444
  ngx_conf_merge_msec_value(conf->upstream_config.connect_timeout,
429
445
  prev->upstream_config.connect_timeout, 12000000);
@@ -583,6 +599,10 @@ passenger_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
583
599
  | NGX_HTTP_UPSTREAM_FT_OFF;
584
600
  }
585
601
 
602
+ if (conf->upstream_config.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {
603
+ conf->upstream_config.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;
604
+ }
605
+
586
606
  if (conf->upstream_config.cache_methods == 0) {
587
607
  conf->upstream_config.cache_methods = prev->upstream_config.cache_methods;
588
608
  }
@@ -1039,7 +1059,14 @@ const ngx_command_t passenger_commands[] = {
1039
1059
  NULL },
1040
1060
 
1041
1061
  { ngx_string("passenger_ruby"),
1042
- NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LIF_CONF | NGX_CONF_TAKE1,
1062
+ NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
1063
+ ngx_conf_set_str_slot,
1064
+ NGX_HTTP_MAIN_CONF_OFFSET,
1065
+ offsetof(passenger_main_conf_t, default_ruby),
1066
+ NULL },
1067
+
1068
+ { ngx_string("passenger_ruby"),
1069
+ NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LIF_CONF | NGX_CONF_TAKE1,
1043
1070
  ngx_conf_set_str_slot,
1044
1071
  NGX_HTTP_LOC_CONF_OFFSET,
1045
1072
  offsetof(passenger_loc_conf_t, ruby),