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.
- checksums.yaml +8 -8
- checksums.yaml.gz.asc +7 -7
- data.tar.gz.asc +7 -7
- data/CHANGELOG +30 -0
- data/CONTRIBUTORS +2 -0
- data/Gemfile.lock +1 -1
- data/bin/passenger-status +13 -15
- data/build/cxx_tests.rb +14 -1
- data/build/preprocessor.rb +4 -2
- data/debian.template/control.template +2 -2
- data/doc/Security of user switching support.txt +2 -2
- data/doc/Users guide Apache.idmap.txt +6 -4
- data/doc/Users guide Apache.txt +20 -1
- data/doc/Users guide Nginx.idmap.txt +5 -3
- data/doc/Users guide Nginx.txt +22 -2
- data/ext/apache2/Configuration.cpp +6 -0
- data/ext/apache2/Configuration.hpp +4 -1
- data/ext/apache2/Hooks.cpp +1 -0
- data/ext/common/Constants.h +4 -2
- data/ext/common/Constants.h.erb +1 -1
- data/ext/common/DataStructures/LString.h +10 -0
- data/ext/common/ServerKit/Channel.h +1 -1
- data/ext/common/ServerKit/Context.h +2 -21
- data/ext/common/ServerKit/CookieUtils.h +246 -0
- data/ext/common/ServerKit/FdSourceChannel.h +10 -0
- data/ext/common/ServerKit/FileBufferedChannel.h +173 -17
- data/ext/common/ServerKit/FileBufferedFdSinkChannel.h +33 -1
- data/ext/common/ServerKit/HeaderTable.h +3 -1
- data/ext/common/ServerKit/HttpServer.h +36 -8
- data/ext/common/ServerKit/Server.h +1 -0
- data/ext/common/Utils.cpp +2 -1
- data/ext/common/Utils/DateParsing.h +15 -2
- data/ext/common/Utils/JsonUtils.h +39 -1
- data/ext/common/agents/HelperAgent/Main.cpp +4 -2
- data/ext/common/agents/HelperAgent/OptionParser.h +14 -2
- data/ext/common/agents/HelperAgent/RequestHandler.h +22 -8
- data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +92 -11
- data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +3 -1
- data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +9 -5
- data/ext/common/agents/HelperAgent/RequestHandler/Request.h +1 -0
- data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +27 -13
- data/ext/common/agents/HelperAgent/ResponseCache.h +91 -34
- data/ext/common/agents/LoggingAgent/AdminServer.h +21 -1
- data/ext/nginx/CacheLocationConfig.c +20 -0
- data/ext/nginx/Configuration.c +130 -24
- data/ext/nginx/Configuration.h +2 -1
- data/ext/nginx/ConfigurationCommands.c +10 -0
- data/ext/nginx/ConfigurationFields.h +2 -0
- data/ext/nginx/ContentHandler.c +1 -6
- data/ext/nginx/CreateLocationConfig.c +5 -0
- data/ext/nginx/MergeLocationConfig.c +6 -0
- data/ext/nginx/StaticContentHandler.c +3 -9
- data/ext/nginx/ngx_http_passenger_module.c +2 -1
- data/ext/ruby/extconf.rb +5 -4
- data/lib/phusion_passenger.rb +2 -2
- data/lib/phusion_passenger/constants.rb +2 -1
- data/lib/phusion_passenger/nginx/config_options.rb +5 -1
- data/lib/phusion_passenger/rack/thread_handler_extension.rb +3 -1
- data/lib/phusion_passenger/ruby_core_enhancements.rb +3 -4
- data/lib/phusion_passenger/standalone/start_command.rb +5 -1
- data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +10 -3
- data/resources/templates/standalone/config.erb +2 -1
- data/test/cxx/DateParsingTest.cpp +75 -0
- data/test/cxx/ResponseCacheTest.cpp +322 -0
- data/test/cxx/ServerKit/CookieUtilsTest.cpp +274 -0
- data/test/cxx/ServerKit/HttpServerTest.cpp +77 -0
- data/test/stub/rails3.0/Gemfile.lock +2 -2
- data/test/stub/rails3.1/Gemfile.lock +2 -2
- data/test/stub/rails3.2/Gemfile.lock +2 -2
- data/test/stub/rails4.0/Gemfile.lock +2 -2
- data/test/stub/rails4.1/Gemfile.lock +2 -2
- metadata +6 -2
- 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 =
|
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->
|
897
|
-
case Request::
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
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),
|
data/ext/common/Utils.cpp
CHANGED
@@ -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.
|
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-
|
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") *
|
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-
|
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"] = (
|
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"] = (
|
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"] = (
|
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["
|
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-
|
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
|
-
|
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
|
-
|
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()
|
142
|
-
|
143
|
-
|
144
|
-
|
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, "
|
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);
|