passenger 5.0.0.beta2 → 5.0.0.beta3

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 (73) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/CHANGELOG +30 -0
  5. data/CONTRIBUTORS +2 -0
  6. data/Gemfile.lock +1 -1
  7. data/bin/passenger-status +13 -15
  8. data/build/cxx_tests.rb +14 -1
  9. data/build/preprocessor.rb +4 -2
  10. data/debian.template/control.template +2 -2
  11. data/doc/Security of user switching support.txt +2 -2
  12. data/doc/Users guide Apache.idmap.txt +6 -4
  13. data/doc/Users guide Apache.txt +20 -1
  14. data/doc/Users guide Nginx.idmap.txt +5 -3
  15. data/doc/Users guide Nginx.txt +22 -2
  16. data/ext/apache2/Configuration.cpp +6 -0
  17. data/ext/apache2/Configuration.hpp +4 -1
  18. data/ext/apache2/Hooks.cpp +1 -0
  19. data/ext/common/Constants.h +4 -2
  20. data/ext/common/Constants.h.erb +1 -1
  21. data/ext/common/DataStructures/LString.h +10 -0
  22. data/ext/common/ServerKit/Channel.h +1 -1
  23. data/ext/common/ServerKit/Context.h +2 -21
  24. data/ext/common/ServerKit/CookieUtils.h +246 -0
  25. data/ext/common/ServerKit/FdSourceChannel.h +10 -0
  26. data/ext/common/ServerKit/FileBufferedChannel.h +173 -17
  27. data/ext/common/ServerKit/FileBufferedFdSinkChannel.h +33 -1
  28. data/ext/common/ServerKit/HeaderTable.h +3 -1
  29. data/ext/common/ServerKit/HttpServer.h +36 -8
  30. data/ext/common/ServerKit/Server.h +1 -0
  31. data/ext/common/Utils.cpp +2 -1
  32. data/ext/common/Utils/DateParsing.h +15 -2
  33. data/ext/common/Utils/JsonUtils.h +39 -1
  34. data/ext/common/agents/HelperAgent/Main.cpp +4 -2
  35. data/ext/common/agents/HelperAgent/OptionParser.h +14 -2
  36. data/ext/common/agents/HelperAgent/RequestHandler.h +22 -8
  37. data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +92 -11
  38. data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +3 -1
  39. data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +9 -5
  40. data/ext/common/agents/HelperAgent/RequestHandler/Request.h +1 -0
  41. data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +27 -13
  42. data/ext/common/agents/HelperAgent/ResponseCache.h +91 -34
  43. data/ext/common/agents/LoggingAgent/AdminServer.h +21 -1
  44. data/ext/nginx/CacheLocationConfig.c +20 -0
  45. data/ext/nginx/Configuration.c +130 -24
  46. data/ext/nginx/Configuration.h +2 -1
  47. data/ext/nginx/ConfigurationCommands.c +10 -0
  48. data/ext/nginx/ConfigurationFields.h +2 -0
  49. data/ext/nginx/ContentHandler.c +1 -6
  50. data/ext/nginx/CreateLocationConfig.c +5 -0
  51. data/ext/nginx/MergeLocationConfig.c +6 -0
  52. data/ext/nginx/StaticContentHandler.c +3 -9
  53. data/ext/nginx/ngx_http_passenger_module.c +2 -1
  54. data/ext/ruby/extconf.rb +5 -4
  55. data/lib/phusion_passenger.rb +2 -2
  56. data/lib/phusion_passenger/constants.rb +2 -1
  57. data/lib/phusion_passenger/nginx/config_options.rb +5 -1
  58. data/lib/phusion_passenger/rack/thread_handler_extension.rb +3 -1
  59. data/lib/phusion_passenger/ruby_core_enhancements.rb +3 -4
  60. data/lib/phusion_passenger/standalone/start_command.rb +5 -1
  61. data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +10 -3
  62. data/resources/templates/standalone/config.erb +2 -1
  63. data/test/cxx/DateParsingTest.cpp +75 -0
  64. data/test/cxx/ResponseCacheTest.cpp +322 -0
  65. data/test/cxx/ServerKit/CookieUtilsTest.cpp +274 -0
  66. data/test/cxx/ServerKit/HttpServerTest.cpp +77 -0
  67. data/test/stub/rails3.0/Gemfile.lock +2 -2
  68. data/test/stub/rails3.1/Gemfile.lock +2 -2
  69. data/test/stub/rails3.2/Gemfile.lock +2 -2
  70. data/test/stub/rails4.0/Gemfile.lock +2 -2
  71. data/test/stub/rails4.1/Gemfile.lock +2 -2
  72. metadata +6 -2
  73. metadata.gz.asc +7 -7
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2014 Phusion
3
+ * Copyright (c) 2014-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -193,6 +193,21 @@ public:
193
193
  return watcher.fd;
194
194
  }
195
195
 
196
+ OXT_FORCE_INLINE
197
+ unsigned int getBytesBuffered() const {
198
+ return FileBufferedChannel::getBytesBuffered();
199
+ }
200
+
201
+ OXT_FORCE_INLINE
202
+ boost::uint64_t getBytesBufferedOnDisk() const {
203
+ return FileBufferedChannel::getBytesBufferedOnDisk();
204
+ }
205
+
206
+ OXT_FORCE_INLINE
207
+ boost::uint64_t getTotalBytesBuffered() const {
208
+ return FileBufferedChannel::getTotalBytesBuffered();
209
+ }
210
+
196
211
  OXT_FORCE_INLINE
197
212
  bool ended() const {
198
213
  return FileBufferedChannel::ended();
@@ -208,17 +223,34 @@ public:
208
223
  return FileBufferedChannel::getHooks();
209
224
  }
210
225
 
226
+ OXT_FORCE_INLINE
211
227
  void setHooks(Hooks *hooks) {
212
228
  FileBufferedChannel::setHooks(hooks);
213
229
  }
214
230
 
231
+ OXT_FORCE_INLINE
232
+ Callback getBuffersFlushedCallback() {
233
+ return FileBufferedChannel::getBuffersFlushedCallback();
234
+ }
235
+
236
+ OXT_FORCE_INLINE
215
237
  void setBuffersFlushedCallback(Callback callback) {
216
238
  FileBufferedChannel::setBuffersFlushedCallback(callback);
217
239
  }
218
240
 
241
+ OXT_FORCE_INLINE
242
+ Callback getDataFlushedCallback() const {
243
+ return FileBufferedChannel::getDataFlushedCallback();
244
+ }
245
+
246
+ OXT_FORCE_INLINE
219
247
  void setDataFlushedCallback(Callback callback) {
220
248
  FileBufferedChannel::setDataFlushedCallback(callback);
221
249
  }
250
+
251
+ Json::Value inspectAsJson() const {
252
+ return FileBufferedChannel::inspectAsJson();
253
+ }
222
254
  };
223
255
 
224
256
 
@@ -272,9 +272,11 @@ public:
272
272
  }
273
273
  LString::Part *part = header->val.start;
274
274
  header->val.start = NULL;
275
+ header->val.size = 0;
275
276
  while (part != NULL) {
277
+ LString::Part *next = part->next;
276
278
  psg_lstr_append_part(&cell->header->val, part);
277
- part = part->next;
279
+ part = next;
278
280
  }
279
281
  return;
280
282
  } else {
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2014 Phusion
3
+ * Copyright (c) 2014-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -858,6 +858,10 @@ protected:
858
858
  // Call doneWithCurrentRequest() when data flushed
859
859
  SKC_TRACE(c, 2, "Waiting until output is flushed");
860
860
  req->httpState = Request::FLUSHING_OUTPUT;
861
+ // If the request body is not fully read at this time,
862
+ // then ensure that onClientDataReceived() discards any
863
+ // request body data that we receive from now on.
864
+ req->wantKeepAlive = canKeepAlive(req);
861
865
  }
862
866
 
863
867
  return true;
@@ -891,15 +895,31 @@ protected:
891
895
 
892
896
  // Moved outside switch() so that the CPU branch predictor can do its work
893
897
  if (req->httpState == Request::PARSING_HEADERS) {
898
+ assert(!req->ended());
894
899
  return processClientDataWhenParsingHeaders(client, req, buffer, errcode);
895
900
  } else {
896
- switch (req->httpState) {
897
- case Request::PARSING_BODY:
898
- return processClientDataWhenParsingBody(client, req, buffer, errcode);
899
- case Request::PARSING_CHUNKED_BODY:
900
- return processClientDataWhenParsingChunkedBody(client, req, buffer, errcode);
901
- case Request::UPGRADED:
902
- return processClientDataWhenUpgraded(client, req, buffer, errcode);
901
+ switch (req->bodyType) {
902
+ case Request::RBT_CONTENT_LENGTH:
903
+ if (req->ended()) {
904
+ assert(!req->wantKeepAlive);
905
+ return Channel::Result(buffer.size(), true);
906
+ } else {
907
+ return processClientDataWhenParsingBody(client, req, buffer, errcode);
908
+ }
909
+ case Request::RBT_CHUNKED:
910
+ if (req->ended()) {
911
+ assert(!req->wantKeepAlive);
912
+ return Channel::Result(buffer.size(), true);
913
+ } else {
914
+ return processClientDataWhenParsingChunkedBody(client, req, buffer, errcode);
915
+ }
916
+ case Request::RBT_UPGRADE:
917
+ if (req->ended()) {
918
+ assert(!req->wantKeepAlive);
919
+ return Channel::Result(buffer.size(), true);
920
+ } else {
921
+ return processClientDataWhenUpgraded(client, req, buffer, errcode);
922
+ }
903
923
  default:
904
924
  P_BUG("Invalid request HTTP state " << (int) req->httpState);
905
925
  // Never reached
@@ -1022,6 +1042,14 @@ protected:
1022
1042
  req->bodyChannel.deinitialize();
1023
1043
  }
1024
1044
 
1045
+
1046
+ /***** Misc *****/
1047
+
1048
+ OXT_FORCE_INLINE
1049
+ static FileBufferedChannel::Callback getClientOutputDataFlushedCallback() {
1050
+ return _onClientOutputDataFlushed;
1051
+ }
1052
+
1025
1053
  public:
1026
1054
  HttpServer(Context *context)
1027
1055
  : ParentClass(context),
@@ -982,6 +982,7 @@ public:
982
982
  doc["name"] = clientName;
983
983
  doc["number"] = client->number;
984
984
  doc["refcount"] = client->refcount.load(boost::memory_order_relaxed);
985
+ doc["output_channel_state"] = client->output.inspectAsJson();
985
986
 
986
987
  return doc;
987
988
  }
@@ -812,7 +812,8 @@ prestartWebApps(const ResourceLocator &locator, const string &ruby,
812
812
  it->c_str(),
813
813
  (char *) 0);
814
814
  e = errno;
815
- fprintf(stderr, "Cannot execute '%s %s': %s (%d)\n",
815
+ fprintf(stderr, "Cannot execute '%s %s %s': %s (%d)\n",
816
+ ruby.c_str(),
816
817
  prespawnScript.c_str(), it->c_str(),
817
818
  strerror(e), e);
818
819
  fflush(stderr);
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2014 Phusion
3
+ * Copyright (c) 2014-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -50,6 +50,7 @@ inline bool parseImfFixdate_zone(const char **pos, const char *end, int &zone);
50
50
 
51
51
  /**
52
52
  * Parses an IMF-fixdate, as defined by RFC 5322.
53
+ * Returns whether parsing succeeded.
53
54
  */
54
55
  inline bool
55
56
  parseImfFixdate(const char *date, const char *end, struct tm &tm, int &zone) {
@@ -57,7 +58,7 @@ parseImfFixdate(const char *date, const char *end, struct tm &tm, int &zone) {
57
58
  // It's too complicated and nobody uses CFWS.
58
59
 
59
60
  tm.tm_yday = -1;
60
- tm.tm_yday = 0; // Pretend that DST is not in effect. Caller should use time zone information.
61
+ tm.tm_isdst = 0;
61
62
 
62
63
  if (!parseImfFixdate_dayOfWeek(&date, end, tm)) {
63
64
  return false;
@@ -72,6 +73,18 @@ parseImfFixdate(const char *date, const char *end, struct tm &tm, int &zone) {
72
73
  return parseImfFixdate_time(&date, end, tm, zone);
73
74
  }
74
75
 
76
+ /**
77
+ * Converts a parsed IMF-fixdate, as outputted by `parseImfFixdate()`,
78
+ * into a Unix timestamp.
79
+ */
80
+ inline time_t
81
+ parsedDateToTimestamp(struct tm &tm, int zone) {
82
+ time_t result = mktime(&tm);
83
+ result -= zone / 100 * 60 * 60 + zone % 100 * 60;
84
+ result += tm.tm_gmtoff;
85
+ return result;
86
+ }
87
+
75
88
  inline void
76
89
  skipImfFixdate_FWS(const char **pos, const char *end) {
77
90
  while (*pos < end && (**pos == '\r' || **pos == '\n' || **pos == ' ' || **pos == '\t')) {
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2014 Phusion
3
+ * Copyright (c) 2014-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -26,6 +26,8 @@
26
26
  #define _PASSENGER_UTILS_JSON_UTILS_H_
27
27
 
28
28
  #include <string>
29
+ #include <cstdio>
30
+ #include <cstddef>
29
31
  #include <StaticString.h>
30
32
  #include <Utils/json.h>
31
33
  #include <Utils/StrIntUtils.h>
@@ -69,6 +71,42 @@ timeToJson(unsigned long long timestamp) {
69
71
  return doc;
70
72
  }
71
73
 
74
+ inline string
75
+ formatFloat(double val) {
76
+ char buf[64];
77
+ int size = snprintf(buf, sizeof(buf), "%.1f", val);
78
+ return string(buf, size);
79
+ }
80
+
81
+ inline Json::Value
82
+ byteSizeToJson(size_t size) {
83
+ Json::Value doc;
84
+ doc["bytes"] = (Json::UInt64) size;
85
+ if (size < 1024) {
86
+ doc["human_readable"] = toString(size) + " bytes";
87
+ } else if (size < 1024 * 1024) {
88
+ doc["human_readable"] = formatFloat(size / 1024.0) + " KB";
89
+ } else {
90
+ doc["human_readable"] = formatFloat(size / 1024.0 / 1024.0) + " MB";
91
+ }
92
+ return doc;
93
+ }
94
+
95
+ inline Json::Value
96
+ signedByteSizeToJson(long long size) {
97
+ Json::Value doc;
98
+ long long absSize = (size < 0) ? -size : size;
99
+ doc["bytes"] = (Json::Int64) size;
100
+ if (absSize < 1024) {
101
+ doc["human_readable"] = toString(size) + " bytes";
102
+ } else if (absSize < 1024 * 1024) {
103
+ doc["human_readable"] = formatFloat(size / 1024.0) + " KB";
104
+ } else {
105
+ doc["human_readable"] = formatFloat(size / 1024.0 / 1024.0) + " MB";
106
+ }
107
+ return doc;
108
+ }
109
+
72
110
  } // namespace Passenger
73
111
 
74
112
  #endif /* _PASSENGER_UTILS_JSON_UTILS_H_ */
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010-2014 Phusion
3
+ * Copyright (c) 2010-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -504,7 +504,7 @@ initializeNonPrivilegedWorkingObjects() {
504
504
  wo->appPool = boost::make_shared<Pool>(wo->spawnerFactory, agentsOptions);
505
505
  wo->appPool->initialize();
506
506
  wo->appPool->setMax(options.getInt("max_pool_size"));
507
- wo->appPool->setMaxIdleTime(options.getInt("pool_idle_time") * 1000000);
507
+ wo->appPool->setMaxIdleTime(options.getInt("pool_idle_time") * 1000000ULL);
508
508
  wo->appPool->enableSelfChecking(options.getBool("selfchecks"));
509
509
  wo->appPool->abortLongRunningConnectionsCallback = abortLongRunningConnections;
510
510
 
@@ -975,8 +975,10 @@ setAgentsOptionsDefaults() {
975
975
  options.setDefaultInt("stat_throttle_rate", DEFAULT_STAT_THROTTLE_RATE);
976
976
  options.setDefault("server_software", SERVER_TOKEN_NAME "/" PASSENGER_VERSION);
977
977
  options.setDefaultBool("show_version_in_header", true);
978
+ options.setDefault("sticky_sessions_cookie_name", DEFAULT_STICKY_SESSIONS_COOKIE_NAME);
978
979
  options.setDefaultBool("turbocaching", true);
979
980
  options.setDefault("data_buffer_dir", getSystemTempDir());
981
+ options.setDefaultInt("response_buffer_high_watermark", DEFAULT_RESPONSE_BUFFER_HIGH_WATERMARK);
980
982
  options.setDefaultBool("selfchecks", false);
981
983
  options.setDefaultBool("server_graceful_exit", true);
982
984
  options.setDefaultInt("server_threads", boost::thread::hardware_concurrency());
@@ -108,8 +108,6 @@ serverUsage() {
108
108
  printf(" Force friendly error pages to be always on\n");
109
109
  printf(" --disable-friendly-error-pages\n");
110
110
  printf(" Force friendly error pages to be always off\n");
111
- printf(" --disable-turbocaching\n");
112
- printf(" Disable turbocaching\n");
113
111
  printf("\n");
114
112
  printf(" --ruby PATH Default Ruby interpreter to use.\n");
115
113
  printf("\n");
@@ -123,6 +121,14 @@ serverUsage() {
123
121
  printf(" may be idle. Default: %d\n", DEFAULT_POOL_IDLE_TIME);
124
122
  printf(" --min-instances N Minimum number of application processes. Default: 1\n");
125
123
  printf("\n");
124
+ printf("Request handling options (optional):\n");
125
+ printf(" --sticky-sessions-cookie-name NAME\n");
126
+ printf(" Cookie name to use for sticky sessions\n");
127
+ printf(" --vary-turbocache-by-cookie NAME\n");
128
+ printf(" Vary the turbocache by the cookie of the given name\n");
129
+ printf(" --disable-turbocaching\n");
130
+ printf(" Disable turbocaching\n");
131
+ printf("\n");
126
132
  printf("Other options (optional):\n");
127
133
  printf(" --log-file PATH Log to the given file.\n");
128
134
  printf(" --log-level LEVEL Logging level. Default: %d\n", DEFAULT_LOG_LEVEL);
@@ -262,6 +268,12 @@ parseServerOption(int argc, const char *argv[], int &i, VariantMap &options) {
262
268
  } else if (p.isFlag(argv[i], '\0', "--disable-friendly-error-pages")) {
263
269
  options.set("friendly_error_pages", "false");
264
270
  i++;
271
+ } else if (p.isValueFlag(argc, i, argv[i], '\0', "--sticky-sessions-cookie-name")) {
272
+ options.set("sticky_sessions_cookie_name", argv[i + 1]);
273
+ i += 2;
274
+ } else if (p.isValueFlag(argc, i, argv[i], '\0', "--vary-turbocache-by-cookie")) {
275
+ options.set("vary_turbocache_by_cookie", argv[i + 1]);
276
+ i += 2;
265
277
  } else if (p.isFlag(argv[i], '\0', "--disable-turbocaching")) {
266
278
  options.setBool("turbocaching", false);
267
279
  i++;
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2011-2014 Phusion
3
+ * Copyright (c) 2011-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -189,6 +189,7 @@ private:
189
189
  static const unsigned int MAX_SESSION_CHECKOUT_TRY = 10;
190
190
 
191
191
  unsigned int statThrottleRate;
192
+ unsigned int responseBufferHighWatermark;
192
193
  BenchmarkMode benchmarkMode: 3;
193
194
  bool singleAppMode: 1;
194
195
  bool showVersionInHeader: 1;
@@ -206,6 +207,8 @@ private:
206
207
  StaticString defaultServerName;
207
208
  StaticString defaultServerPort;
208
209
  StaticString serverSoftware;
210
+ StaticString defaultStickySessionsCookieName;
211
+ StaticString defaultVaryTurbocacheByCookie;
209
212
 
210
213
  HashedStaticString PASSENGER_APP_GROUP_NAME;
211
214
  HashedStaticString PASSENGER_MAX_REQUESTS;
@@ -231,6 +234,7 @@ private:
231
234
  StaticString serverLogName;
232
235
 
233
236
  friend class TurboCaching<Request>;
237
+ friend class ResponseCache<Request>;
234
238
  struct ev_check checkWatcher;
235
239
  TurboCaching<Request> turboCaching;
236
240
 
@@ -259,6 +263,7 @@ public:
259
263
  : ParentClass(context),
260
264
 
261
265
  statThrottleRate(_agentsOptions->getInt("stat_throttle_rate")),
266
+ responseBufferHighWatermark(_agentsOptions->getInt("response_buffer_high_watermark")),
262
267
  benchmarkMode(parseBenchmarkMode(_agentsOptions->get("benchmark_mode", false))),
263
268
  singleAppMode(false),
264
269
  showVersionInHeader(_agentsOptions->getBool("show_version_in_header")),
@@ -307,6 +312,13 @@ public:
307
312
  agentsOptions->get("default_server_port"));
308
313
  serverSoftware = psg_pstrdup(stringPool,
309
314
  agentsOptions->get("server_software"));
315
+ defaultStickySessionsCookieName = psg_pstrdup(stringPool,
316
+ agentsOptions->get("sticky_sessions_cookie_name"));
317
+
318
+ if (agentsOptions->has("vary_turbocache_by_cookie")) {
319
+ defaultVaryTurbocacheByCookie = psg_pstrdup(stringPool,
320
+ agentsOptions->get("vary_turbocache_by_cookie"));
321
+ }
310
322
 
311
323
  generateServerLogName(_threadNumber);
312
324
 
@@ -465,6 +477,7 @@ public:
465
477
  virtual Json::Value inspectRequestStateAsJson(const Request *req) const {
466
478
  Json::Value doc = ParentClass::inspectRequestStateAsJson(req);
467
479
  Json::Value flags;
480
+ const AppResponse *resp = &req->appResponse;
468
481
 
469
482
  if (req->startedAt != 0) {
470
483
  doc["started_at"] = timeToJson(req->startedAt * 1000000.0);
@@ -482,13 +495,12 @@ public:
482
495
  doc["flags"] = flags;
483
496
 
484
497
  if (req->requestBodyBuffering) {
485
- doc["body_bytes_buffered"] = (Json::Value::UInt64) req->bodyBytesBuffered;
498
+ doc["body_bytes_buffered"] = byteSizeToJson(req->bodyBytesBuffered);
486
499
  }
487
500
 
488
501
  if (req->session != NULL) {
489
502
  Json::Value &sessionDoc = doc["session"] = Json::Value(Json::objectValue);
490
503
  const Session *session = req->session.get();
491
- const AppResponse *resp = &req->appResponse;
492
504
 
493
505
  if (req->session->isClosed()) {
494
506
  sessionDoc["closed"] = true;
@@ -496,24 +508,26 @@ public:
496
508
  sessionDoc["pid"] = session->getPid();
497
509
  sessionDoc["gupid"] = session->getGupid().toString();
498
510
  }
511
+ }
499
512
 
513
+ if (req->session != NULL || resp->httpState != AppResponse::PARSING_HEADERS) {
500
514
  doc["app_response_http_state"] = resp->getHttpStateString();
501
515
  doc["app_response_http_major"] = resp->httpMajor;
502
516
  doc["app_response_http_minor"] = resp->httpMinor;
503
517
  doc["app_response_want_keep_alive"] = resp->wantKeepAlive;
504
518
  doc["app_response_body_type"] = resp->getBodyTypeString();
505
519
  doc["app_response_body_fully_read"] = resp->bodyFullyRead();
506
- doc["app_response_body_already_read"] = (Json::Value::UInt64)
507
- resp->bodyAlreadyRead;
520
+ doc["app_response_body_already_read"] = byteSizeToJson(
521
+ resp->bodyAlreadyRead);
508
522
  if (resp->httpState != AppResponse::ERROR) {
509
523
  if (resp->bodyType == AppResponse::RBT_CONTENT_LENGTH) {
510
- doc["app_response_content_length"] = (Json::Value::UInt64)
511
- resp->aux.bodyInfo.contentLength;
524
+ doc["app_response_content_length"] = byteSizeToJson(
525
+ resp->aux.bodyInfo.contentLength);
512
526
  } else if (resp->bodyType == AppResponse::RBT_CHUNKED) {
513
527
  doc["app_response_end_chunk_reached"] = resp->aux.bodyInfo.endChunkReached;
514
528
  }
515
529
  } else {
516
- doc["parse_error"] = ServerKit::getErrorDesc(resp->aux.parseError);
530
+ doc["app_response_parse_error"] = ServerKit::getErrorDesc(resp->aux.parseError);
517
531
  }
518
532
  }
519
533
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2011-2014 Phusion
3
+ * Copyright (c) 2011-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -73,11 +73,12 @@ onAppSourceData(Client *client, Request *req, const MemoryKit::mbuf &buffer, int
73
73
  onAppResponseBegin(client, req);
74
74
  return Channel::Result(ret, false);
75
75
  case AppResponse::PARSING_BODY_WITH_LENGTH:
76
+ SKC_TRACE(client, 2, "Expecting an app response body with fixed length");
77
+ onAppResponseBegin(client, req);
78
+ return Channel::Result(ret, false);
76
79
  case AppResponse::PARSING_BODY_UNTIL_EOF:
77
- SKC_TRACE(client, 2,
78
- ((resp->httpState == AppResponse::PARSING_BODY_WITH_LENGTH)
79
- ? "Expecting an app response body with fixed length"
80
- : "Expecting app response body until end of stream"));
80
+ SKC_TRACE(client, 2, "Expecting app response body until end of stream");
81
+ req->wantKeepAlive = false;
81
82
  onAppResponseBegin(client, req);
82
83
  return Channel::Result(ret, false);
83
84
  case AppResponse::PARSING_CHUNKED_BODY:
@@ -87,7 +88,7 @@ onAppSourceData(Client *client, Request *req, const MemoryKit::mbuf &buffer, int
87
88
  return Channel::Result(ret, false);
88
89
  case AppResponse::UPGRADED:
89
90
  SKC_TRACE(client, 2, "Application upgraded connection");
90
- assert(!req->wantKeepAlive);
91
+ req->wantKeepAlive = false;
91
92
  onAppResponseBegin(client, req);
92
93
  return Channel::Result(ret, false);
93
94
  case AppResponse::ONEHUNDRED_CONTINUE:
@@ -138,10 +139,14 @@ onAppSourceData(Client *client, Request *req, const MemoryKit::mbuf &buffer, int
138
139
  UPDATE_TRACE_POINT();
139
140
  writeResponseAndMarkForTurboCaching(client, req,
140
141
  MemoryKit::mbuf(buffer, 0, remaining));
141
- if (!req->ended() && resp->bodyFullyRead()) {
142
- SKC_TRACE(client, 2, "End of application response body reached");
143
- handleAppResponseBodyEnd(client, req);
144
- endRequest(&client, &req);
142
+ if (!req->ended()) {
143
+ if (resp->bodyFullyRead()) {
144
+ SKC_TRACE(client, 2, "End of application response body reached");
145
+ handleAppResponseBodyEnd(client, req);
146
+ endRequest(&client, &req);
147
+ } else {
148
+ maybeThrottleAppSource(client, req);
149
+ }
145
150
  }
146
151
  } else {
147
152
  UPDATE_TRACE_POINT();
@@ -191,6 +196,7 @@ onAppSourceData(Client *client, Request *req, const MemoryKit::mbuf &buffer, int
191
196
  case ServerKit::HttpChunkedEvent::DATA:
192
197
  assert(!event.end);
193
198
  writeResponseAndMarkForTurboCaching(client, req, event.data);
199
+ maybeThrottleAppSource(client, req);
194
200
  return Channel::Result(event.consumed, false);
195
201
  case ServerKit::HttpChunkedEvent::END:
196
202
  assert(event.end);
@@ -216,6 +222,7 @@ onAppSourceData(Client *client, Request *req, const MemoryKit::mbuf &buffer, int
216
222
  assert(!event.end);
217
223
  writeResponse(client, MemoryKit::mbuf(buffer, 0, event.consumed));
218
224
  markResponsePartForTurboCaching(client, req, event.data);
225
+ maybeThrottleAppSource(client, req);
219
226
  return Channel::Result(event.consumed, false);
220
227
  case ServerKit::HttpChunkedEvent::END:
221
228
  assert(event.end);
@@ -261,6 +268,7 @@ onAppSourceData(Client *client, Request *req, const MemoryKit::mbuf &buffer, int
261
268
  buffer.start, buffer.size())) << "\"");
262
269
  resp->bodyAlreadyRead += buffer.size();
263
270
  writeResponseAndMarkForTurboCaching(client, req, buffer);
271
+ maybeThrottleAppSource(client, req);
264
272
  return Channel::Result(buffer.size(), false);
265
273
  } else if (errcode == 0 || errcode == ECONNRESET) {
266
274
  // EOF
@@ -360,7 +368,7 @@ void prepareAppResponseCaching(Client *client, Request *req) {
360
368
  if (turboCaching.isEnabled() && !req->cacheKey.empty()) {
361
369
  TRACE_POINT();
362
370
  AppResponse *resp = &req->appResponse;
363
- SKC_TRACE(client, 2, "Preparing response caching");
371
+ SKC_TRACE(client, 2, "Turbocache: preparing response caching");
364
372
  if (turboCaching.responseCache.requestAllowsStoring(req)
365
373
  && turboCaching.responseCache.prepareRequestForStoring(req))
366
374
  {
@@ -379,6 +387,11 @@ void prepareAppResponseCaching(Client *client, Request *req) {
379
387
  turboCaching.responseCache.invalidate(req);
380
388
  req->cacheKey = HashedStaticString();
381
389
  SKC_TRACE(client, 2, "Turbocache entries:\n" << turboCaching.responseCache.inspect());
390
+ } else {
391
+ SKC_TRACE(client, 2, "Turbocache: response not eligible for turbocaching");
392
+ // Decrease store success ratio.
393
+ turboCaching.responseCache.incStores();
394
+ req->cacheKey = HashedStaticString();
382
395
  }
383
396
  }
384
397
  }
@@ -835,6 +848,74 @@ markResponsePartForTurboCaching(Client *client, Request *req, const MemoryKit::m
835
848
  }
836
849
  }
837
850
 
851
+ void
852
+ maybeThrottleAppSource(Client *client, Request *req) {
853
+ if (!req->ended()) {
854
+ assert(client->output.getBuffersFlushedCallback() == NULL);
855
+ assert(client->output.getDataFlushedCallback() == getClientOutputDataFlushedCallback());
856
+ if (responseBufferHighWatermark > 0
857
+ && client->output.getTotalBytesBuffered() >= responseBufferHighWatermark)
858
+ {
859
+ SKC_TRACE(client, 2, "Application is sending response data quicker than the client "
860
+ "can keep up with. Throttling application socket");
861
+ client->output.setDataFlushedCallback(_outputDataFlushed);
862
+ req->appSource.stop();
863
+ } else if (client->output.passedThreshold()) {
864
+ SKC_TRACE(client, 2, "Application is sending response data quicker than the on-disk "
865
+ "buffer can keep up with (currently buffered " << client->output.getBytesBuffered() <<
866
+ " bytes). Throttling application socket");
867
+ client->output.setBuffersFlushedCallback(_outputBuffersFlushed);
868
+ req->appSource.stop();
869
+ }
870
+ }
871
+ }
872
+
873
+ static void
874
+ _outputBuffersFlushed(FileBufferedChannel *_channel) {
875
+ FileBufferedFdSinkChannel *channel = reinterpret_cast<FileBufferedFdSinkChannel *>(_channel);
876
+ Client *client = static_cast<Client *>(static_cast<
877
+ ServerKit::BaseClient *>(channel->getHooks()->userData));
878
+ Request *req = static_cast<Request *>(client->currentRequest);
879
+ RequestHandler *self = static_cast<RequestHandler *>(getServerFromClient(client));
880
+ if (client->connected() && req != NULL) {
881
+ self->outputBuffersFlushed(client, req);
882
+ }
883
+ }
884
+
885
+ void
886
+ outputBuffersFlushed(Client *client, Request *req) {
887
+ if (!req->ended()) {
888
+ assert(!req->appSource.isStarted());
889
+ SKC_TRACE(client, 2, "Buffered response data has been written to disk. Resuming application socket");
890
+ client->output.setBuffersFlushedCallback(NULL);
891
+ req->appSource.start();
892
+ }
893
+ }
894
+
895
+ static void
896
+ _outputDataFlushed(FileBufferedChannel *_channel) {
897
+ FileBufferedFdSinkChannel *channel = reinterpret_cast<FileBufferedFdSinkChannel *>(_channel);
898
+ Client *client = static_cast<Client *>(static_cast<
899
+ ServerKit::BaseClient *>(channel->getHooks()->userData));
900
+ Request *req = static_cast<Request *>(client->currentRequest);
901
+ RequestHandler *self = static_cast<RequestHandler *>(getServerFromClient(client));
902
+
903
+ getClientOutputDataFlushedCallback()(_channel);
904
+ if (client->connected() && req != NULL) {
905
+ self->outputDataFlushed(client, req);
906
+ }
907
+ }
908
+
909
+ void
910
+ outputDataFlushed(Client *client, Request *req) {
911
+ if (!req->ended()) {
912
+ assert(!req->appSource.isStarted());
913
+ SKC_TRACE(client, 2, "The client is ready to receive more data. Resuming application socket");
914
+ client->output.setDataFlushedCallback(getClientOutputDataFlushedCallback());
915
+ req->appSource.start();
916
+ }
917
+ }
918
+
838
919
  void
839
920
  handleAppResponseBodyEnd(Client *client, Request *req) {
840
921
  keepAliveAppConnection(client, req);