passenger 5.0.25 → 5.0.26

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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +20 -0
  3. data/CONTRIBUTORS +1 -0
  4. data/build/cxx_dependency_map.rb +7338 -7104
  5. data/build/cxx_tests.rb +3 -3
  6. data/build/misc.rb +1 -0
  7. data/dev/index_cxx_dependencies.rb +3 -2
  8. data/resources/templates/standalone/config.erb +1 -1
  9. data/resources/templates/standalone/http.erb +1 -0
  10. data/resources/templates/standalone/server.erb +1 -0
  11. data/src/agent/Core/ApplicationPool/AbstractSession.h +83 -0
  12. data/src/agent/Core/ApplicationPool/Common.h +6 -4
  13. data/src/agent/Core/ApplicationPool/Options.h +4 -1
  14. data/src/agent/Core/ApplicationPool/Pool.h +2 -2
  15. data/src/agent/Core/ApplicationPool/Pool/AnalyticsCollection.cpp +3 -6
  16. data/src/agent/Core/ApplicationPool/Pool/GeneralUtils.cpp +3 -3
  17. data/src/agent/Core/ApplicationPool/Session.h +15 -27
  18. data/src/agent/Core/ApplicationPool/TestSession.h +188 -0
  19. data/src/agent/Core/Controller.h +15 -6
  20. data/src/agent/Core/Controller/CheckoutSession.cpp +13 -5
  21. data/src/agent/Core/Controller/ForwardResponse.cpp +20 -2
  22. data/src/agent/Core/Controller/Hooks.cpp +15 -2
  23. data/src/agent/Core/Controller/InitRequest.cpp +5 -1
  24. data/src/agent/Core/Controller/InitializationAndShutdown.cpp +1 -0
  25. data/src/agent/Core/Controller/Request.h +11 -4
  26. data/src/agent/Core/Controller/SendRequest.cpp +34 -13
  27. data/src/agent/Core/Controller/StateInspectionAndConfiguration.cpp +2 -2
  28. data/src/agent/Core/CoreMain.cpp +27 -1
  29. data/src/agent/Core/OptionParser.h +11 -1
  30. data/src/agent/Core/SpawningKit/DirectSpawner.h +1 -0
  31. data/src/agent/Core/SpawningKit/SmartSpawner.h +1 -0
  32. data/src/agent/Core/SpawningKit/Spawner.h +21 -1
  33. data/src/agent/SpawnPreparer/SpawnPreparerMain.cpp +1 -1
  34. data/src/agent/UstRouter/OptionParser.h +7 -1
  35. data/src/agent/UstRouter/UstRouterMain.cpp +27 -1
  36. data/src/cxx_supportlib/Algorithms/MovingAverage.h +223 -0
  37. data/src/cxx_supportlib/Constants.h +2 -2
  38. data/src/cxx_supportlib/DataStructures/StringKeyTable.h +96 -40
  39. data/src/cxx_supportlib/ResourceLocator.h +33 -14
  40. data/src/cxx_supportlib/ServerKit/Channel.h +198 -69
  41. data/src/cxx_supportlib/ServerKit/Errors.h +6 -1
  42. data/src/cxx_supportlib/ServerKit/HttpRequest.h +20 -1
  43. data/src/cxx_supportlib/ServerKit/HttpServer.h +124 -32
  44. data/src/cxx_supportlib/ServerKit/Server.h +65 -1
  45. data/src/cxx_supportlib/Utils/IOUtils.cpp +12 -22
  46. data/src/cxx_supportlib/Utils/JsonUtils.h +87 -1
  47. data/src/cxx_supportlib/Utils/StrIntUtils.cpp +16 -1
  48. data/src/cxx_supportlib/Utils/StrIntUtils.h +31 -1
  49. data/src/cxx_supportlib/Utils/VariantMap.h +6 -1
  50. data/src/cxx_supportlib/WatchdogLauncher.h +17 -9
  51. data/src/cxx_supportlib/vendor-copy/libuv/AUTHORS +43 -0
  52. data/src/cxx_supportlib/vendor-copy/libuv/ChangeLog +350 -1
  53. data/src/cxx_supportlib/vendor-copy/libuv/Makefile.am +9 -1
  54. data/src/cxx_supportlib/vendor-copy/libuv/README.md +48 -0
  55. data/src/cxx_supportlib/vendor-copy/libuv/checksparse.sh +1 -0
  56. data/src/cxx_supportlib/vendor-copy/libuv/common.gypi +5 -5
  57. data/src/cxx_supportlib/vendor-copy/libuv/configure.ac +2 -1
  58. data/src/cxx_supportlib/vendor-copy/libuv/gyp_uv.py +0 -3
  59. data/src/cxx_supportlib/vendor-copy/libuv/include/uv-version.h +5 -1
  60. data/src/cxx_supportlib/vendor-copy/libuv/include/uv.h +30 -3
  61. data/src/cxx_supportlib/vendor-copy/libuv/src/fs-poll.c +3 -3
  62. data/src/cxx_supportlib/vendor-copy/libuv/src/inet.c +0 -4
  63. data/src/cxx_supportlib/vendor-copy/libuv/src/queue.h +17 -1
  64. data/src/cxx_supportlib/vendor-copy/libuv/src/threadpool.c +10 -10
  65. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/aix.c +84 -166
  66. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/android-ifaddrs.c +11 -11
  67. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/async.c +7 -1
  68. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/atomic-ops.h +17 -0
  69. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/core.c +140 -21
  70. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/darwin.c +15 -11
  71. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/dl.c +4 -7
  72. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/freebsd.c +52 -37
  73. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/fs.c +181 -60
  74. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/fsevents.c +39 -34
  75. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/getaddrinfo.c +4 -4
  76. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/internal.h +3 -1
  77. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/kqueue.c +12 -4
  78. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/linux-core.c +38 -15
  79. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/linux-inotify.c +36 -8
  80. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/linux-syscalls.c +4 -4
  81. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/linux-syscalls.h +2 -2
  82. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/loop-watcher.c +6 -1
  83. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/loop.c +28 -8
  84. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/netbsd.c +18 -16
  85. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/openbsd.c +18 -16
  86. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/pipe.c +3 -3
  87. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/process.c +18 -6
  88. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/proctitle.c +2 -2
  89. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/pthread-fixes.c +1 -0
  90. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/signal.c +2 -0
  91. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/stream.c +47 -30
  92. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/sunos.c +13 -11
  93. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/tcp.c +43 -8
  94. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/thread.c +21 -15
  95. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/tty.c +16 -2
  96. data/src/cxx_supportlib/vendor-copy/libuv/src/unix/udp.c +54 -14
  97. data/src/cxx_supportlib/vendor-copy/libuv/src/uv-common.c +104 -21
  98. data/src/cxx_supportlib/vendor-copy/libuv/src/uv-common.h +14 -1
  99. data/src/cxx_supportlib/vendor-copy/libuv/src/version.c +1 -5
  100. data/src/cxx_supportlib/vendor-copy/libuv/uv.gyp +22 -1
  101. data/src/nginx_module/CacheLocationConfig.c +52 -0
  102. data/src/nginx_module/CacheLocationConfig.c.erb +13 -1
  103. data/src/nginx_module/Configuration.c +1 -0
  104. data/src/nginx_module/Configuration.h +1 -0
  105. data/src/nginx_module/ConfigurationCommands.c +20 -0
  106. data/src/nginx_module/ConfigurationFields.h +4 -0
  107. data/src/nginx_module/CreateLocationConfig.c +8 -0
  108. data/src/nginx_module/MergeLocationConfig.c +12 -0
  109. data/src/nginx_module/config +31 -13
  110. data/src/nginx_module/ngx_http_passenger_module.c +4 -0
  111. data/src/ruby_supportlib/phusion_passenger.rb +1 -1
  112. data/src/ruby_supportlib/phusion_passenger/constants.rb +1 -1
  113. data/src/ruby_supportlib/phusion_passenger/nginx/config_options.rb +11 -1
  114. data/src/ruby_supportlib/phusion_passenger/platform_info/apache.rb +6 -1
  115. data/src/ruby_supportlib/phusion_passenger/rack/thread_handler_extension.rb +32 -31
  116. data/src/ruby_supportlib/phusion_passenger/standalone/config_options_list.rb +13 -2
  117. data/src/ruby_supportlib/phusion_passenger/standalone/config_utils.rb +1 -0
  118. data/src/ruby_supportlib/phusion_passenger/standalone/start_command/builtin_engine.rb +6 -1
  119. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core.rb +6 -0
  120. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/api.rb +29 -19
  121. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/context.rb +2 -2
  122. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter.rb +2 -3
  123. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/simple_json.rb +2 -1
  124. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/spec_helper.rb +2 -0
  125. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/time_point.rb +3 -17
  126. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/transaction.rb +7 -10
  127. data/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/utils.rb +11 -9
  128. metadata +5 -2
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2012-2014 Phusion Holding B.V.
3
+ * Copyright (c) 2012-2016 Phusion Holding B.V.
4
4
  *
5
5
  * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
6
  * trademarks of Phusion Holding B.V.
@@ -52,6 +52,9 @@ enum Error {
52
52
  ERROR_SECURE_HEADER_NOT_ALLOWED = -1017,
53
53
  NORMAL_HEADER_NOT_ALLOWED_AFTER_SECURITY_PASSWORD = -1018,
54
54
 
55
+ // HttpServer special errors
56
+ EARLY_EOF_DETECTED = -1020,
57
+
55
58
  // Error codes below -2000 are http_parser errors
56
59
  HTTP_PARSER_ERRNO_BEGIN = -2000,
57
60
  };
@@ -87,6 +90,8 @@ getErrorDesc(int errcode) {
87
90
  return "A secure header was provided, but no security password was provided";
88
91
  case NORMAL_HEADER_NOT_ALLOWED_AFTER_SECURITY_PASSWORD:
89
92
  return "A normal header was encountered after the security password header";
93
+ case EARLY_EOF_DETECTED:
94
+ return "The client connection is closed before the request is done processing";
90
95
  default:
91
96
  if (errcode <= HTTP_PARSER_ERRNO_BEGIN) {
92
97
  return http_errno_description((enum http_errno)
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2014-2015 Phusion Holding B.V.
3
+ * Copyright (c) 2014-2016 Phusion Holding B.V.
4
4
  *
5
5
  * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
6
  * trademarks of Phusion Holding B.V.
@@ -102,6 +102,7 @@ public:
102
102
  http_method method: 5;
103
103
  bool wantKeepAlive: 1;
104
104
  bool responseBegun: 1;
105
+ bool detectingNextRequestEarlyReadError: 1;
105
106
 
106
107
  boost::atomic<int> refcount;
107
108
 
@@ -139,6 +140,9 @@ public:
139
140
  } aux;
140
141
  boost::uint64_t bodyAlreadyRead;
141
142
 
143
+ ev_tstamp lastDataReceiveTime;
144
+ ev_tstamp lastDataSendTime;
145
+
142
146
  /**
143
147
  * The start index of the '?' character in `path`. -1 when it doesn't exist.
144
148
  */
@@ -149,6 +153,21 @@ public:
149
153
  */
150
154
  int bodyError;
151
155
 
156
+ /**
157
+ * When a request body read error, or a client socket EOF, has been detected
158
+ * after the current request body has already fully received, the error code is
159
+ * temporarily stored here so that it may be processed at the next request. The
160
+ * error is not passed to the bodyChannel immediately because it isn't an error
161
+ * part of the current request's body. But users of HttpServer can still query
162
+ * this field to see that an error is imminent, and may choose to abort early.
163
+ *
164
+ * The value is either the body read error code, or EARLY_EOF_DETECTED. The
165
+ * latter means that a client socket EOF has been detected.
166
+ *
167
+ * A value of 0 means that everthing is ok.
168
+ */
169
+ int nextRequestEarlyReadError;
170
+
152
171
 
153
172
  BaseHttpRequest()
154
173
  : refcount(1),
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2014-2015 Phusion Holding B.V.
3
+ * Copyright (c) 2014-2016 Phusion Holding B.V.
4
4
  *
5
5
  * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
6
  * trademarks of Phusion Holding B.V.
@@ -32,6 +32,7 @@
32
32
  #include <oxt/macros.hpp>
33
33
  #include <algorithm>
34
34
  #include <cstdio>
35
+ #include <cmath>
35
36
  #include <cassert>
36
37
  #include <pthread.h>
37
38
  #include <Logging.h>
@@ -41,10 +42,12 @@
41
42
  #include <ServerKit/HttpRequestRef.h>
42
43
  #include <ServerKit/HttpHeaderParser.h>
43
44
  #include <ServerKit/HttpChunkedBodyParser.h>
45
+ #include <Algorithms/MovingAverage.h>
44
46
  #include <Utils/SystemTime.h>
45
47
  #include <Utils/StrIntUtils.h>
46
48
  #include <Utils/HttpConstants.h>
47
49
  #include <Utils/Hasher.h>
50
+ #include <Utils/SystemTime.h>
48
51
 
49
52
  namespace Passenger {
50
53
  namespace ServerKit {
@@ -65,7 +68,8 @@ public:
65
68
 
66
69
  FreeRequestList freeRequests;
67
70
  unsigned int freeRequestCount, requestFreelistLimit;
68
- unsigned long totalRequestsBegun;
71
+ unsigned long totalRequestsBegun, lastTotalRequestsBegun;
72
+ double requestBeginSpeed1m, requestBeginSpeed1h;
69
73
 
70
74
  private:
71
75
  /***** Types and nested classes *****/
@@ -209,6 +213,7 @@ private:
209
213
  assert(c->currentRequest != NULL);
210
214
  Request *req = c->currentRequest;
211
215
  bool keepAlive = canKeepAlive(req);
216
+ int nextRequestEarlyReadError = req->nextRequestEarlyReadError;
212
217
 
213
218
  P_ASSERT_EQ(req->httpState, Request::WAITING_FOR_REFERENCES);
214
219
  assert(req->pool != NULL);
@@ -221,6 +226,9 @@ private:
221
226
  if (keepAlive) {
222
227
  SKC_TRACE(c, 3, "Keeping alive connection, handling next request");
223
228
  handleNextRequest(c);
229
+ if (nextRequestEarlyReadError != 0) {
230
+ onClientDataReceived(c, MemoryKit::mbuf(), nextRequestEarlyReadError);
231
+ }
224
232
  } else {
225
233
  SKC_TRACE(c, 3, "Not keeping alive connection, disconnecting client");
226
234
  this->disconnect(client);
@@ -278,7 +286,7 @@ private:
278
286
 
279
287
  switch (req->httpState) {
280
288
  case Request::COMPLETE:
281
- client->input.stop();
289
+ req->detectingNextRequestEarlyReadError = true;
282
290
  onRequestBegin(client, req);
283
291
  return Channel::Result(ret, false);
284
292
  case Request::PARSING_BODY:
@@ -338,39 +346,34 @@ private:
338
346
 
339
347
  boost::uint64_t maxRemaining, remaining;
340
348
 
349
+ assert(req->aux.bodyInfo.contentLength > 0);
341
350
  maxRemaining = req->aux.bodyInfo.contentLength - req->bodyAlreadyRead;
351
+ assert(maxRemaining > 0);
342
352
  remaining = std::min<boost::uint64_t>(buffer.size(), maxRemaining);
343
353
  req->bodyAlreadyRead += remaining;
344
354
  SKC_TRACE(client, 3, "Request body: " <<
345
355
  req->bodyAlreadyRead << " of " <<
346
356
  req->aux.bodyInfo.contentLength << " bytes already read");
347
357
 
348
- if (remaining > 0) {
349
- req->bodyChannel.feed(MemoryKit::mbuf(buffer, 0, remaining));
350
- if (req->ended()) {
351
- return Channel::Result(remaining, false);
352
- }
358
+ req->bodyChannel.feed(MemoryKit::mbuf(buffer, 0, remaining));
359
+ if (req->ended()) {
360
+ return Channel::Result(remaining, false);
361
+ }
353
362
 
354
- if (req->bodyChannel.acceptingInput()) {
355
- if (req->bodyFullyRead()) {
356
- SKC_TRACE(client, 2, "End of request body reached");
357
- client->input.stop();
358
- req->bodyChannel.feed(MemoryKit::mbuf());
359
- }
360
- return Channel::Result(remaining, false);
361
- } else if (req->bodyChannel.mayAcceptInputLater()) {
362
- client->input.stop();
363
- req->bodyChannel.consumedCallback =
364
- onRequestBodyChannelConsumed;
365
- return Channel::Result(remaining, false);
366
- } else {
367
- return Channel::Result(remaining, true);
363
+ if (req->bodyChannel.acceptingInput()) {
364
+ if (req->bodyFullyRead()) {
365
+ SKC_TRACE(client, 2, "End of request body reached");
366
+ req->detectingNextRequestEarlyReadError = true;
367
+ req->bodyChannel.feed(MemoryKit::mbuf());
368
368
  }
369
- } else {
370
- SKC_TRACE(client, 2, "End of request body reached");
369
+ return Channel::Result(remaining, false);
370
+ } else if (req->bodyChannel.mayAcceptInputLater()) {
371
371
  client->input.stop();
372
- req->bodyChannel.feed(MemoryKit::mbuf());
373
- return Channel::Result(0, false);
372
+ req->bodyChannel.consumedCallback =
373
+ onRequestBodyChannelConsumed;
374
+ return Channel::Result(remaining, false);
375
+ } else {
376
+ return Channel::Result(remaining, true);
374
377
  }
375
378
  } else if (errcode == 0) {
376
379
  // Premature EOF. This cannot be an expected EOF because we
@@ -430,7 +433,7 @@ private:
430
433
  }
431
434
  case HttpChunkedEvent::END:
432
435
  assert(event.end);
433
- client->input.stop();
436
+ req->detectingNextRequestEarlyReadError = true;
434
437
  req->aux.bodyInfo.endChunkReached = true;
435
438
  req->bodyChannel.feed(MemoryKit::mbuf());
436
439
  return Channel::Result(event.consumed, false);
@@ -583,6 +586,38 @@ private:
583
586
  createChunkedBodyParser(req).initialize();
584
587
  }
585
588
 
589
+ bool detectNextRequestEarlyReadError(Client *client, Request *req, const MemoryKit::mbuf &buffer,
590
+ int errcode)
591
+ {
592
+ if (req->detectingNextRequestEarlyReadError) {
593
+ // When we have previously fully read the expected request body,
594
+ // the above flag is set to true. This tells us to detect whether
595
+ // an EOF or an error on the socket has occurred before we are done
596
+ // processing the request.
597
+
598
+ req->detectingNextRequestEarlyReadError = false;
599
+ client->input.stop();
600
+
601
+ if (!req->ended() && buffer.empty()) {
602
+ if (errcode == 0) {
603
+ SKC_TRACE(client, 3, "Early read EOF detected");
604
+ req->nextRequestEarlyReadError = EARLY_EOF_DETECTED;
605
+ } else {
606
+ SKC_TRACE(client, 3, "Early body receive error detected: "
607
+ << getErrorDesc(errcode) << " (errno=" << errcode << ")");
608
+ req->nextRequestEarlyReadError = errcode;
609
+ }
610
+ onNextRequestEarlyReadError(client, req, req->nextRequestEarlyReadError);
611
+ } else {
612
+ SKC_TRACE(client, 3, "No early read EOF or body receive error detected");
613
+ }
614
+
615
+ return true;
616
+ } else {
617
+ return false;
618
+ }
619
+ }
620
+
586
621
 
587
622
  /***** Channel callbacks *****/
588
623
 
@@ -678,36 +713,44 @@ protected:
678
713
  assert(client->currentRequest != NULL);
679
714
  Request *req = client->currentRequest;
680
715
  RequestRef ref(req, __FILE__, __LINE__);
716
+ bool ended = req->ended();
717
+
718
+ if (!ended) {
719
+ req->lastDataReceiveTime = ev_now(this->getLoop());
720
+ }
721
+ if (detectNextRequestEarlyReadError(client, req, buffer, errcode)) {
722
+ return Channel::Result(0, false);
723
+ }
681
724
 
682
725
  // Moved outside switch() so that the CPU branch predictor can do its work
683
726
  if (req->httpState == Request::PARSING_HEADERS) {
684
- assert(!req->ended());
727
+ assert(!ended);
685
728
  return processClientDataWhenParsingHeaders(client, req, buffer, errcode);
686
729
  } else {
687
730
  switch (req->bodyType) {
688
731
  case Request::RBT_CONTENT_LENGTH:
689
- if (req->ended()) {
732
+ if (ended) {
690
733
  assert(!req->wantKeepAlive);
691
734
  return Channel::Result(buffer.size(), true);
692
735
  } else {
693
736
  return processClientDataWhenParsingBody(client, req, buffer, errcode);
694
737
  }
695
738
  case Request::RBT_CHUNKED:
696
- if (req->ended()) {
739
+ if (ended) {
697
740
  assert(!req->wantKeepAlive);
698
741
  return Channel::Result(buffer.size(), true);
699
742
  } else {
700
743
  return processClientDataWhenParsingChunkedBody(client, req, buffer, errcode);
701
744
  }
702
745
  case Request::RBT_UPGRADE:
703
- if (req->ended()) {
746
+ if (ended) {
704
747
  assert(!req->wantKeepAlive);
705
748
  return Channel::Result(buffer.size(), true);
706
749
  } else {
707
750
  return processClientDataWhenUpgraded(client, req, buffer, errcode);
708
751
  }
709
752
  default:
710
- P_BUG("Invalid request HTTP state " << (int) req->httpState);
753
+ P_BUG("Invalid request body type " << (int) req->bodyType);
711
754
  // Never reached
712
755
  return Channel::Result(0, false);
713
756
  }
@@ -737,6 +780,29 @@ protected:
737
780
  || client->currentRequest->upgraded();
738
781
  }
739
782
 
783
+ virtual void onUpdateStatistics() {
784
+ ParentClass::onUpdateStatistics();
785
+ ev_tstamp now = ev_now(this->getLoop());
786
+ ev_tstamp duration = now - this->lastStatisticsUpdateTime;
787
+
788
+ // Statistics are updated about every 5 seconds, so about 12 updates
789
+ // per minute. We want the old average to decay to 5% after 1 minute
790
+ // and 1 hour, respectively, so:
791
+ // 1 minute: 1 - exp(ln(0.05) / 12) = 0.22092219194555585
792
+ // 1 hour : 1 - exp(ln(0.05) / (60 * 12)) = 0.0041520953856636345
793
+ requestBeginSpeed1m = expMovingAverage(requestBeginSpeed1m,
794
+ (totalRequestsBegun - lastTotalRequestsBegun) / duration,
795
+ 0.22092219194555585);
796
+ requestBeginSpeed1h = expMovingAverage(requestBeginSpeed1h,
797
+ (totalRequestsBegun - lastTotalRequestsBegun) / duration,
798
+ 0.0041520953856636345);
799
+ }
800
+
801
+ virtual void onFinalizeStatisticsUpdate() {
802
+ ParentClass::onFinalizeStatisticsUpdate();
803
+ lastTotalRequestsBegun = totalRequestsBegun;
804
+ }
805
+
740
806
 
741
807
  /***** New hooks *****/
742
808
 
@@ -762,6 +828,10 @@ protected:
762
828
  return Channel::Result(buffer.size(), false);
763
829
  }
764
830
 
831
+ virtual void onNextRequestEarlyReadError(Client *client, Request *req, int errcode) {
832
+ // Do nothing.
833
+ }
834
+
765
835
  virtual bool supportsUpgrade(Client *client, Request *req) {
766
836
  return false;
767
837
  }
@@ -790,6 +860,7 @@ protected:
790
860
  req->method = HTTP_GET;
791
861
  req->wantKeepAlive = false;
792
862
  req->responseBegun = false;
863
+ req->detectingNextRequestEarlyReadError = false;
793
864
  req->parserState.headerParser = headerParserStatePool.construct();
794
865
  createRequestHeaderParser(this->getContext(), req).initialize();
795
866
  if (OXT_UNLIKELY(req->pool == NULL)) {
@@ -801,7 +872,11 @@ protected:
801
872
  req->bodyChannel.reinitialize();
802
873
  req->aux.bodyInfo.contentLength = 0; // Sets the entire union to 0.
803
874
  req->bodyAlreadyRead = 0;
875
+ req->lastDataReceiveTime = 0;
876
+ req->lastDataSendTime = 0;
804
877
  req->queryStringIndex = -1;
878
+ req->bodyError = 0;
879
+ req->nextRequestEarlyReadError = 0;
805
880
  }
806
881
 
807
882
  /**
@@ -860,6 +935,9 @@ public:
860
935
  freeRequestCount(0),
861
936
  requestFreelistLimit(1024),
862
937
  totalRequestsBegun(0),
938
+ lastTotalRequestsBegun(0),
939
+ requestBeginSpeed1m(-1),
940
+ requestBeginSpeed1h(-1),
863
941
  headerParserStatePool(16, 256)
864
942
  {
865
943
  STAILQ_INIT(&freeRequests);
@@ -928,6 +1006,7 @@ public:
928
1006
 
929
1007
  void writeResponse(Client *client, const MemoryKit::mbuf &buffer) {
930
1008
  client->currentRequest->responseBegun = true;
1009
+ client->currentRequest->lastDataSendTime = ev_now(this->getLoop());
931
1010
  client->output.feedWithoutRefGuard(buffer);
932
1011
  }
933
1012
 
@@ -1120,6 +1199,12 @@ public:
1120
1199
  Json::Value doc = ParentClass::inspectStateAsJson();
1121
1200
  doc["free_request_count"] = freeRequestCount;
1122
1201
  doc["total_requests_begun"] = (Json::UInt64) totalRequestsBegun;
1202
+ doc["request_begin_speed"]["1m"] = averageSpeedToJson(
1203
+ capFloatPrecision(requestBeginSpeed1m * 60),
1204
+ "minute", "1 minute", -1);
1205
+ doc["request_begin_speed"]["1h"] = averageSpeedToJson(
1206
+ capFloatPrecision(requestBeginSpeed1h * 60),
1207
+ "minute", "1 hour", -1);
1123
1208
  return doc;
1124
1209
  }
1125
1210
 
@@ -1149,6 +1234,8 @@ public:
1149
1234
  doc["request_body_fully_read"] = req->bodyFullyRead();
1150
1235
  doc["request_body_already_read"] = (Json::Value::UInt64) req->bodyAlreadyRead;
1151
1236
  doc["response_begun"] = req->responseBegun;
1237
+ doc["last_data_receive_time"] = timeToJson(req->lastDataReceiveTime * 1000000);
1238
+ doc["last_data_send_time"] = timeToJson(req->lastDataSendTime * 1000000);
1152
1239
  doc["method"] = http_method_str(req->method);
1153
1240
  if (req->httpState != Request::ERROR) {
1154
1241
  if (req->bodyType == Request::RBT_CONTENT_LENGTH) {
@@ -1162,6 +1249,11 @@ public:
1162
1249
  doc["parse_error"] = getErrorDesc(req->aux.parseError);
1163
1250
  }
1164
1251
 
1252
+ if (req->nextRequestEarlyReadError != 0) {
1253
+ doc["next_request_early_read_error"] = getErrorDesc(req->nextRequestEarlyReadError)
1254
+ + string(" (errno=") + toString(req->nextRequestEarlyReadError) + ")";
1255
+ }
1256
+
1165
1257
  string str;
1166
1258
  str.reserve(req->path.size);
1167
1259
  part = req->path.start;
@@ -56,10 +56,12 @@
56
56
  #include <ServerKit/Hooks.h>
57
57
  #include <ServerKit/Client.h>
58
58
  #include <ServerKit/ClientRef.h>
59
+ #include <Algorithms/MovingAverage.h>
59
60
  #include <Utils.h>
60
61
  #include <Utils/ScopeGuard.h>
61
62
  #include <Utils/StrIntUtils.h>
62
63
  #include <Utils/IOUtils.h>
64
+ #include <Utils/SystemTime.h>
63
65
 
64
66
  namespace Passenger {
65
67
  namespace ServerKit {
@@ -212,8 +214,11 @@ public:
212
214
  FreeClientList freeClients;
213
215
  ClientList activeClients, disconnectedClients;
214
216
  unsigned int freeClientCount, activeClientCount, disconnectedClientCount;
215
- unsigned long totalClientsAccepted;
217
+ unsigned int peakActiveClientCount;
218
+ unsigned long totalClientsAccepted, lastTotalClientsAccepted;
216
219
  unsigned long long totalBytesConsumed;
220
+ ev_tstamp lastStatisticsUpdateTime;
221
+ double clientAcceptSpeed1m, clientAcceptSpeed1h;
217
222
 
218
223
  private:
219
224
  Context *ctx;
@@ -221,6 +226,7 @@ private:
221
226
  uint8_t nEndpoints: 3;
222
227
  bool accept4Available: 1;
223
228
  ev::timer acceptResumptionWatcher;
229
+ ev::timer statisticsUpdateWatcher;
224
230
  ev::io endpoints[SERVER_KIT_MAX_SERVER_ENDPOINTS];
225
231
 
226
232
 
@@ -335,6 +341,16 @@ private:
335
341
  }
336
342
  }
337
343
 
344
+ void onStatisticsUpdateTimeout(ev::timer &timer, int revents) {
345
+ TRACE_POINT();
346
+
347
+ this->onUpdateStatistics();
348
+ this->onFinalizeStatisticsUpdate();
349
+
350
+ timer.repeat = timeToNextMultipleD(5, ev_now(this->getLoop()));
351
+ timer.again();
352
+ }
353
+
338
354
  unsigned int getNextClientNumber() {
339
355
  return nextClientNumber++;
340
356
  }
@@ -442,6 +458,9 @@ private:
442
458
  TRACE_POINT();
443
459
  compact(LVL_INFO);
444
460
 
461
+ acceptResumptionWatcher.stop();
462
+ statisticsUpdateWatcher.stop();
463
+
445
464
  SKS_NOTICE("Shutdown finished");
446
465
  serverState = FINISHED_SHUTDOWN;
447
466
  if (shutdownFinishCallback != NULL) {
@@ -513,6 +532,8 @@ protected:
513
532
  virtual void onClientsAccepted(Client **clients, unsigned int size) {
514
533
  unsigned int i;
515
534
 
535
+ peakActiveClientCount = std::max(peakActiveClientCount, activeClientCount);
536
+
516
537
  for (i = 0; i < size; i++) {
517
538
  Client *client = clients[i];
518
539
 
@@ -572,6 +593,29 @@ protected:
572
593
  return LVL_WARN;
573
594
  }
574
595
 
596
+ virtual void onUpdateStatistics() {
597
+ SKS_DEBUG("Updating statistics");
598
+ ev_tstamp now = ev_now(this->getLoop());
599
+ ev_tstamp duration = now - lastStatisticsUpdateTime;
600
+
601
+ // Statistics are updated about every 5 seconds, so about 12 updates
602
+ // per minute. We want the old average to decay to 5% after 1 minute
603
+ // and 1 hour, respectively, so:
604
+ // 1 minute: 1 - exp(ln(0.05) / 12) = 0.22092219194555585
605
+ // 1 hour : 1 - exp(ln(0.05) / (60 * 12)) = 0.0041520953856636345
606
+ clientAcceptSpeed1m = expMovingAverage(clientAcceptSpeed1m,
607
+ (totalClientsAccepted - lastTotalClientsAccepted) / duration,
608
+ 0.22092219194555585);
609
+ clientAcceptSpeed1h = expMovingAverage(clientAcceptSpeed1h,
610
+ (totalClientsAccepted - lastTotalClientsAccepted) / duration,
611
+ 0.0041520953856636345);
612
+ }
613
+
614
+ virtual void onFinalizeStatisticsUpdate() {
615
+ lastTotalClientsAccepted = totalClientsAccepted;
616
+ lastStatisticsUpdateTime = ev_now(this->getLoop());
617
+ }
618
+
575
619
  virtual void reinitializeClient(Client *client, int fd) {
576
620
  client->setConnState(Client::ACTIVE);
577
621
  SKC_TRACE(client, 2, "Client associated with file descriptor: " << fd);
@@ -601,8 +645,13 @@ public:
601
645
  freeClientCount(0),
602
646
  activeClientCount(0),
603
647
  disconnectedClientCount(0),
648
+ peakActiveClientCount(0),
604
649
  totalClientsAccepted(0),
650
+ lastTotalClientsAccepted(0),
605
651
  totalBytesConsumed(0),
652
+ lastStatisticsUpdateTime(ev_time()),
653
+ clientAcceptSpeed1m(-1),
654
+ clientAcceptSpeed1h(-1),
606
655
  ctx(context),
607
656
  nextClientNumber(1),
608
657
  nEndpoints(0),
@@ -611,10 +660,18 @@ public:
611
660
  STAILQ_INIT(&freeClients);
612
661
  TAILQ_INIT(&activeClients);
613
662
  TAILQ_INIT(&disconnectedClients);
663
+
614
664
  acceptResumptionWatcher.set(context->libev->getLoop());
615
665
  acceptResumptionWatcher.set<
616
666
  BaseServer<DerivedServer, Client>,
617
667
  &BaseServer<DerivedServer, Client>::onAcceptResumeTimeout>(this);
668
+
669
+ statisticsUpdateWatcher.set(context->libev->getLoop());
670
+ statisticsUpdateWatcher.set<
671
+ BaseServer<DerivedServer, Client>,
672
+ &BaseServer<DerivedServer, Client>::onStatisticsUpdateTimeout>(this);
673
+ statisticsUpdateWatcher.set(5, 5);
674
+ statisticsUpdateWatcher.start();
618
675
  }
619
676
 
620
677
  virtual ~BaseServer() {
@@ -995,6 +1052,13 @@ public:
995
1052
  doc["active_client_count"] = activeClientCount;
996
1053
  Json::Value &disconnectedClientsDoc = doc["disconnected_clients"] = Json::Value(Json::objectValue);
997
1054
  doc["disconnected_client_count"] = disconnectedClientCount;
1055
+ doc["peak_active_client_count"] = peakActiveClientCount;
1056
+ doc["client_accept_speed"]["1m"] = averageSpeedToJson(
1057
+ capFloatPrecision(clientAcceptSpeed1m * 60),
1058
+ "minute", "1 minute", -1);
1059
+ doc["client_accept_speed"]["1h"] = averageSpeedToJson(
1060
+ capFloatPrecision(clientAcceptSpeed1h * 60),
1061
+ "minute", "1 hour", -1);
998
1062
  doc["total_clients_accepted"] = (Json::UInt64) totalClientsAccepted;
999
1063
  doc["total_bytes_consumed"] = (Json::UInt64) totalBytesConsumed;
1000
1064