passenger 4.0.45 → 4.0.46
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/.editorconfig +19 -0
- data/CHANGELOG +47 -0
- data/CONTRIBUTING.md +9 -1
- data/CONTRIBUTORS +4 -0
- data/Vagrantfile +7 -3
- data/build/agents.rb +1 -0
- data/build/misc.rb +6 -4
- data/dev/vagrant/bashrc +2 -0
- data/doc/Design and Architecture.txt +9 -7
- data/doc/Users guide Apache.idmap.txt +2 -0
- data/doc/Users guide Apache.txt +24 -4
- data/doc/Users guide Nginx.idmap.txt +4 -0
- data/doc/Users guide Nginx.txt +23 -4
- data/doc/images/code_walkthrough.jpg +0 -0
- data/doc/users_guide_snippets/installation.txt +38 -0
- data/ext/common/AgentsStarter.h +6 -1
- data/ext/common/ApplicationPool2/Common.h +17 -2
- data/ext/common/ApplicationPool2/DirectSpawner.h +5 -11
- data/ext/common/ApplicationPool2/DummySpawner.h +2 -4
- data/ext/common/ApplicationPool2/ErrorRenderer.h +119 -0
- data/ext/common/ApplicationPool2/Implementation.cpp +159 -11
- data/ext/common/ApplicationPool2/Options.h +16 -7
- data/ext/common/ApplicationPool2/Pool.h +28 -24
- data/ext/common/ApplicationPool2/Process.h +1 -9
- data/ext/common/ApplicationPool2/SmartSpawner.h +15 -18
- data/ext/common/ApplicationPool2/Spawner.h +18 -14
- data/ext/common/ApplicationPool2/SpawnerFactory.h +12 -30
- data/ext/common/Constants.h +1 -1
- data/ext/common/Exceptions.h +15 -2
- data/ext/common/UnionStation/Core.h +9 -0
- data/ext/common/Utils/JsonUtils.h +53 -0
- data/ext/common/Utils/ProcessMetricsCollector.h +1 -1
- data/ext/common/Utils/SpeedMeter.h +7 -3
- data/ext/common/Utils/SystemMetricsCollector.h +8 -6
- data/ext/common/agents/HelperAgent/Main.cpp +4 -4
- data/ext/common/agents/HelperAgent/RequestHandler.h +115 -56
- data/ext/nginx/ConfigurationCommands.c +1 -1
- data/ext/nginx/ConfigurationCommands.c.erb +6 -1
- data/ext/nginx/ContentHandler.c +2 -1
- data/ext/nginx/config +1 -1
- data/helper-scripts/node-loader.js +23 -0
- data/helper-scripts/wsgi-loader.py +12 -4
- data/lib/phusion_passenger.rb +1 -1
- data/lib/phusion_passenger/active_support3_extensions/init.rb +39 -78
- data/lib/phusion_passenger/constants.rb +3 -1
- data/lib/phusion_passenger/loader_shared_helpers.rb +10 -5
- data/lib/phusion_passenger/nginx/config_options.rb +3 -1
- data/lib/phusion_passenger/packaging.rb +1 -0
- data/lib/phusion_passenger/public_api.rb +108 -16
- data/lib/phusion_passenger/rack/thread_handler_extension.rb +1 -0
- data/lib/phusion_passenger/request_handler.rb +2 -2
- data/lib/phusion_passenger/request_handler/thread_handler.rb +28 -46
- data/lib/phusion_passenger/standalone/command.rb +8 -1
- data/lib/phusion_passenger/standalone/main.rb +0 -1
- data/lib/phusion_passenger/standalone/start_command.rb +4 -0
- data/lib/phusion_passenger/union_station/connection.rb +67 -0
- data/lib/phusion_passenger/{analytics_logger.rb → union_station/core.rb} +55 -256
- data/lib/phusion_passenger/union_station/transaction.rb +168 -0
- data/lib/phusion_passenger/utils.rb +4 -0
- data/lib/phusion_passenger/utils/lock.rb +62 -0
- data/resources/mime.types +1 -0
- data/resources/templates/error_layout.html.template +2 -0
- data/resources/templates/standalone/config.erb +1 -0
- data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +5 -3
- data/test/cxx/ApplicationPool2/PoolTest.cpp +13 -3
- data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +16 -13
- data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +6 -0
- data/test/cxx/FileBackedPipeTest.cpp +1 -1
- data/test/cxx/RequestHandlerTest.cpp +158 -2
- data/test/cxx/ServerInstanceDirTest.cpp +2 -0
- data/test/cxx/TestSupport.h +21 -2
- data/test/cxx/UtilsTest.cpp +1 -0
- data/test/ruby/classic_rails/loader_spec.rb +0 -1
- data/test/ruby/classic_rails/preloader_spec.rb +0 -1
- data/test/ruby/rails3.0/loader_spec.rb +2 -2
- data/test/ruby/rails3.0/preloader_spec.rb +2 -2
- data/test/ruby/rails3.1/loader_spec.rb +2 -2
- data/test/ruby/rails3.1/preloader_spec.rb +2 -2
- data/test/ruby/rails3.2/loader_spec.rb +2 -2
- data/test/ruby/rails3.2/preloader_spec.rb +2 -2
- data/test/ruby/rails4.0/loader_spec.rb +2 -2
- data/test/ruby/rails4.0/preloader_spec.rb +2 -2
- data/test/ruby/request_handler_spec.rb +8 -8
- data/test/ruby/shared/rails/{analytics_logging_extensions_sharedspec.rb → union_station_extensions_sharedspec.rb} +5 -4
- data/test/ruby/union_station_spec.rb +283 -0
- data/test/stub/wsgi/passenger_wsgi.py +41 -5
- metadata +12 -7
- metadata.gz.asc +7 -7
- data/helper-scripts/wsgi-preloader.py +0 -1
- data/lib/phusion_passenger/standalone/package_runtime_command.rb +0 -105
- data/test/ruby/analytics_logger_spec.rb +0 -283
data/ext/common/Constants.h
CHANGED
data/ext/common/Exceptions.h
CHANGED
@@ -255,17 +255,26 @@ class SpawnException: public oxt::tracable_exception {
|
|
255
255
|
public:
|
256
256
|
enum ErrorKind {
|
257
257
|
UNDEFINED_ERROR,
|
258
|
-
/** The preloader failed to start, not due to a wrong protocol message.
|
258
|
+
/** The preloader failed to start, not due to a wrong protocol message.
|
259
|
+
* It did not explicitly supply an error message.
|
260
|
+
*/
|
259
261
|
PRELOADER_STARTUP_ERROR,
|
260
262
|
/** The preloader sent a wrong protocol message during startup. */
|
261
263
|
PRELOADER_STARTUP_PROTOCOL_ERROR,
|
264
|
+
/** The preloader timed out during startup. */
|
262
265
|
PRELOADER_STARTUP_TIMEOUT,
|
266
|
+
/** The preloader failed to start, not due to a wrong protocol message.
|
267
|
+
* It DID explicitly supply an error message. */
|
263
268
|
PRELOADER_STARTUP_EXPLAINABLE_ERROR,
|
264
|
-
/** The application failed to start, not due to a wrong protocol message.
|
269
|
+
/** The application failed to start, not due to a wrong protocol message.
|
270
|
+
* It did not explicitly supply an error message. */
|
265
271
|
APP_STARTUP_ERROR,
|
266
272
|
/** The application sent a wrong protocol message during startup. */
|
267
273
|
APP_STARTUP_PROTOCOL_ERROR,
|
274
|
+
/** The application timed out during startup. */
|
268
275
|
APP_STARTUP_TIMEOUT,
|
276
|
+
/** The application failed to start, not due to a wrong protocol message.
|
277
|
+
* It DID explicitly supply an error message. */
|
269
278
|
APP_STARTUP_EXPLAINABLE_ERROR
|
270
279
|
};
|
271
280
|
|
@@ -342,6 +351,10 @@ public:
|
|
342
351
|
return get(name);
|
343
352
|
}
|
344
353
|
|
354
|
+
void set(const string &name, const string &value) {
|
355
|
+
annotations[name] = value;
|
356
|
+
}
|
357
|
+
|
345
358
|
string get(const string &name) const {
|
346
359
|
map<string, string>::const_iterator it = annotations.find(name);
|
347
360
|
if (it == annotations.end()) {
|
@@ -381,6 +381,9 @@ public:
|
|
381
381
|
// Get a connection to the logging server.
|
382
382
|
ConnectionPtr connection = checkoutConnection();
|
383
383
|
if (connection == NULL) {
|
384
|
+
P_TRACE(2, "Created NULL Union Station transaction: group=" << groupName <<
|
385
|
+
", category=" << category << ", txnId=" <<
|
386
|
+
StaticString(txnId, txnIdEnd - txnId));
|
384
387
|
return createNullTransaction();
|
385
388
|
}
|
386
389
|
|
@@ -395,8 +398,14 @@ public:
|
|
395
398
|
category,
|
396
399
|
unionStationKey);
|
397
400
|
guard.clear();
|
401
|
+
P_TRACE(2, "Created new Union Station transaction: group=" << groupName <<
|
402
|
+
", category=" << category << ", txnId=" <<
|
403
|
+
StaticString(txnId, txnIdEnd - txnId));
|
398
404
|
return transaction;
|
399
405
|
} else {
|
406
|
+
P_TRACE(2, "Created NULL Union Station transaction: group=" << groupName <<
|
407
|
+
", category=" << category << ", txnId=" <<
|
408
|
+
StaticString(txnId, txnIdEnd - txnId));
|
400
409
|
return createNullTransaction();
|
401
410
|
}
|
402
411
|
}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2014 Phusion
|
4
|
+
*
|
5
|
+
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
|
+
*
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
12
|
+
* furnished to do so, subject to the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
15
|
+
* all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
* THE SOFTWARE.
|
24
|
+
*/
|
25
|
+
#ifndef _PASSENGER_UTILS_JSON_UTILS_H_
|
26
|
+
#define _PASSENGER_UTILS_JSON_UTILS_H_
|
27
|
+
|
28
|
+
#include <string>
|
29
|
+
#include <StaticString.h>
|
30
|
+
#include <Utils/json.h>
|
31
|
+
#include <Utils/StrIntUtils.h>
|
32
|
+
|
33
|
+
namespace Passenger {
|
34
|
+
|
35
|
+
using namespace std;
|
36
|
+
|
37
|
+
inline string
|
38
|
+
stringifyJson(const Json::Value &value) {
|
39
|
+
Json::FastWriter writer;
|
40
|
+
string str = writer.write(value);
|
41
|
+
str.erase(str.size() - 1, 1);
|
42
|
+
return str;
|
43
|
+
}
|
44
|
+
|
45
|
+
/** `str` MUST be NULL-terminated! */
|
46
|
+
inline string
|
47
|
+
jsonString(const Passenger::StaticString &str) {
|
48
|
+
return stringifyJson(Json::Value(Json::StaticString(str.data())));
|
49
|
+
}
|
50
|
+
|
51
|
+
} // namespace Passenger
|
52
|
+
|
53
|
+
#endif /* _PASSENGER_UTILS_JSON_UTILS_H_ */
|
@@ -191,7 +191,7 @@ public:
|
|
191
191
|
return count;
|
192
192
|
}
|
193
193
|
|
194
|
-
/** Current speed over the configured window. Returns
|
194
|
+
/** Current speed over the configured window. Returns unknownSpeed() if less than 2
|
195
195
|
* samples have been collected so far.
|
196
196
|
*/
|
197
197
|
double currentSpeed() const {
|
@@ -221,16 +221,20 @@ public:
|
|
221
221
|
}
|
222
222
|
avgWeight /= std::max(1, (int) count - 1 - begin);
|
223
223
|
|
224
|
-
interval = getSample(count - 1).timestamp - getSample(begin).timestamp;
|
224
|
+
interval = getSample((int) count - 1).timestamp - getSample(begin).timestamp;
|
225
225
|
if (interval > 0) {
|
226
226
|
// sum / interval is the speed per average delta interval,
|
227
227
|
// so we extrapolate that over the entire window interval.
|
228
228
|
return (sum / interval) * (window / avgWeight);
|
229
229
|
} else {
|
230
|
-
return
|
230
|
+
return unknownSpeed();
|
231
231
|
}
|
232
232
|
}
|
233
233
|
|
234
|
+
static ValueType unknownSpeed() {
|
235
|
+
return numeric_limits<ValueType>::max();
|
236
|
+
}
|
237
|
+
|
234
238
|
#if 1
|
235
239
|
void debug() const {
|
236
240
|
const unsigned long long timeThreshold = getTimeThreshold();
|
@@ -45,7 +45,6 @@
|
|
45
45
|
#include <sys/sysinfo.h>
|
46
46
|
#include <Exceptions.h>
|
47
47
|
#include <Utils/StringScanning.h>
|
48
|
-
#include <Utils/SpeedMeter.h>
|
49
48
|
#include <Utils/IOUtils.h>
|
50
49
|
#endif
|
51
50
|
#ifdef __APPLE__
|
@@ -69,6 +68,7 @@
|
|
69
68
|
#include <Utils/StrIntUtils.h>
|
70
69
|
#include <Utils/SystemTime.h>
|
71
70
|
#include <Utils/AnsiColorConstants.h>
|
71
|
+
#include <Utils/SpeedMeter.h>
|
72
72
|
|
73
73
|
/*
|
74
74
|
* Useful resources
|
@@ -346,13 +346,15 @@ public:
|
|
346
346
|
time_t boottime;
|
347
347
|
|
348
348
|
/** Speed at which processes are created per second.
|
349
|
-
*
|
349
|
+
* SpeedMeter<unsigned long long>::unknownSpeed() if it's not yet known (because too
|
350
|
+
* few samples have been taken so far).
|
350
351
|
* -1 if there was an error querying this information.
|
351
352
|
* -2 if the OS does not support this metric.
|
352
353
|
*/
|
353
354
|
double forkRate;
|
354
355
|
/** Speed at which the OS swaps in and swaps out data, in KB/sec.
|
355
|
-
*
|
356
|
+
* SpeedMeter<size_t>::unknownSpeed() if it's not yet known
|
357
|
+
* (because too few samples have been taken so far).
|
356
358
|
* -1 if there was an error querying this information.
|
357
359
|
* -2 if the OS does not support this metric.
|
358
360
|
*/
|
@@ -537,7 +539,7 @@ public:
|
|
537
539
|
|
538
540
|
if (forkRate != -2) {
|
539
541
|
stream << "Fork rate : ";
|
540
|
-
if (
|
542
|
+
if (forkRate == SpeedMeter<unsigned long long>::unknownSpeed() || forkRate < 0) {
|
541
543
|
if (options.colors) {
|
542
544
|
stream << ANSI_COLOR_DGRAY;
|
543
545
|
}
|
@@ -648,7 +650,7 @@ public:
|
|
648
650
|
|
649
651
|
if (swapInRate != -2) {
|
650
652
|
stream << "Swap in : ";
|
651
|
-
if (
|
653
|
+
if (swapInRate == SpeedMeter<size_t>::unknownSpeed() || swapInRate < 0) {
|
652
654
|
if (options.colors) {
|
653
655
|
stream << ANSI_COLOR_DGRAY;
|
654
656
|
}
|
@@ -668,7 +670,7 @@ public:
|
|
668
670
|
|
669
671
|
if (swapOutRate != -2) {
|
670
672
|
stream << "Swap out : ";
|
671
|
-
if (
|
673
|
+
if (swapOutRate == SpeedMeter<size_t>::unknownSpeed() || swapOutRate < 0) {
|
672
674
|
if (options.colors) {
|
673
675
|
stream << ANSI_COLOR_DGRAY;
|
674
676
|
}
|
@@ -462,10 +462,10 @@ public:
|
|
462
462
|
UPDATE_TRACE_POINT();
|
463
463
|
unionStationCore = boost::make_shared<UnionStation::Core>(options.loggingAgentAddress,
|
464
464
|
"logging", options.loggingAgentPassword);
|
465
|
-
spawnerFactory = boost::make_shared<SpawnerFactory>(
|
466
|
-
|
467
|
-
|
468
|
-
|
465
|
+
spawnerFactory = boost::make_shared<SpawnerFactory>(generation,
|
466
|
+
boost::make_shared<SpawnerConfig>(resourceLocator, unionStationCore,
|
467
|
+
randomGenerator));
|
468
|
+
pool = boost::make_shared<Pool>(spawnerFactory, &options);
|
469
469
|
pool->initialize();
|
470
470
|
pool->setMax(options.maxPoolSize);
|
471
471
|
pool->setMaxIdleTime(options.poolIdleTime * 1000000);
|
@@ -112,6 +112,7 @@
|
|
112
112
|
#include <boost/shared_ptr.hpp>
|
113
113
|
#include <boost/weak_ptr.hpp>
|
114
114
|
#include <boost/make_shared.hpp>
|
115
|
+
#include <boost/regex.hpp>
|
115
116
|
#include <ev++.h>
|
116
117
|
|
117
118
|
#if defined(__GLIBCXX__) || defined(__APPLE__)
|
@@ -135,6 +136,7 @@
|
|
135
136
|
#include <UnionStation/Transaction.h>
|
136
137
|
#include <UnionStation/ScopeLog.h>
|
137
138
|
#include <ApplicationPool2/Pool.h>
|
139
|
+
#include <ApplicationPool2/ErrorRenderer.h>
|
138
140
|
#include <Utils/StrIntUtils.h>
|
139
141
|
#include <Utils/IOUtils.h>
|
140
142
|
#include <Utils/HttpHeaderBufferer.h>
|
@@ -225,6 +227,8 @@ private:
|
|
225
227
|
sessionCheckoutTry = 0;
|
226
228
|
responseHeaderSeen = false;
|
227
229
|
chunkedResponse = false;
|
230
|
+
responseContentLength = -1;
|
231
|
+
responseBodyAlreadyRead = 0;
|
228
232
|
appRoot.clear();
|
229
233
|
}
|
230
234
|
|
@@ -336,6 +340,21 @@ public:
|
|
336
340
|
|
337
341
|
bool responseHeaderSeen;
|
338
342
|
bool chunkedResponse;
|
343
|
+
/** The size of the response body, set based on the values of
|
344
|
+
* the Content-Length and Transfer-Encoding response headers.
|
345
|
+
* Possible values:
|
346
|
+
*
|
347
|
+
* -1: infinite. Should keep forwarding response body until end of stream.
|
348
|
+
* This is the case for WebSockets or for responses without Content-Length.
|
349
|
+
* Responses with "Transfer-Encoding: chunked" also fall under this
|
350
|
+
* category, though in this case encountering the zero-length chunk is
|
351
|
+
* treated the same as end of stream.
|
352
|
+
* 0 : no client body. Should immediately close connection after forwarding
|
353
|
+
* headers.
|
354
|
+
* >0: Should forward exactly this many bytes of the response body.
|
355
|
+
*/
|
356
|
+
long long responseContentLength;
|
357
|
+
unsigned long long responseBodyAlreadyRead;
|
339
358
|
HttpHeaderBufferer responseHeaderBufferer;
|
340
359
|
Dechunker responseDechunker;
|
341
360
|
|
@@ -586,6 +605,8 @@ public:
|
|
586
605
|
<< indent << "requestIsChunked = " << boolStr(requestIsChunked) << "\n"
|
587
606
|
<< indent << "requestBodyLength = " << requestBodyLength << "\n"
|
588
607
|
<< indent << "requestBodyAlreadyRead = " << requestBodyAlreadyRead << "\n"
|
608
|
+
<< indent << "responseContentLength = " << responseContentLength << "\n"
|
609
|
+
<< indent << "responseBodyAlreadyRead = " << responseBodyAlreadyRead << "\n"
|
589
610
|
<< indent << "clientInput = " << clientInput.get() << " " << clientInput->inspect() << "\n"
|
590
611
|
<< indent << "clientInput started = " << boolStr(clientInput->isStarted()) << "\n"
|
591
612
|
<< indent << "clientBodyBuffer started = " << boolStr(clientBodyBuffer->isStarted()) << "\n"
|
@@ -629,6 +650,7 @@ private:
|
|
629
650
|
HashMap<int, ClientPtr> clients;
|
630
651
|
Timer inactivityTimer;
|
631
652
|
bool accept4Available;
|
653
|
+
boost::regex upgradeHeaderRegex;
|
632
654
|
|
633
655
|
|
634
656
|
void disconnect(const ClientPtr &client) {
|
@@ -774,47 +796,12 @@ private:
|
|
774
796
|
assert(client->state < Client::FORWARDING_BODY_TO_APP);
|
775
797
|
client->state = Client::WRITING_SIMPLE_RESPONSE;
|
776
798
|
|
777
|
-
|
799
|
+
ErrorRenderer renderer(resourceLocator);
|
778
800
|
string data;
|
779
801
|
|
780
802
|
if (friendlyErrorPagesEnabled(client)) {
|
781
803
|
try {
|
782
|
-
|
783
|
-
string errorLayoutFile = templatesDir + "/error_layout.html.template";
|
784
|
-
string generalErrorFile =
|
785
|
-
(e != NULL && e->isHTML())
|
786
|
-
? templatesDir + "/general_error_with_html.html.template"
|
787
|
-
: templatesDir + "/general_error.html.template";
|
788
|
-
string css = readAll(cssFile);
|
789
|
-
StringMap<StaticString> params;
|
790
|
-
|
791
|
-
params.set("CSS", css);
|
792
|
-
params.set("APP_ROOT", client->options.appRoot);
|
793
|
-
params.set("RUBY", client->options.ruby);
|
794
|
-
params.set("ENVIRONMENT", client->options.environment);
|
795
|
-
params.set("MESSAGE", message);
|
796
|
-
params.set("IS_RUBY_APP",
|
797
|
-
(client->options.appType == "classic-rails" || client->options.appType == "rack")
|
798
|
-
? "true" : "false");
|
799
|
-
if (e != NULL) {
|
800
|
-
params.set("TITLE", "Web application could not be started");
|
801
|
-
// Store all SpawnException annotations into 'params',
|
802
|
-
// but convert its name to uppercase.
|
803
|
-
const map<string, string> &annotations = e->getAnnotations();
|
804
|
-
map<string, string>::const_iterator it, end = annotations.end();
|
805
|
-
for (it = annotations.begin(); it != end; it++) {
|
806
|
-
string name = it->first;
|
807
|
-
for (string::size_type i = 0; i < name.size(); i++) {
|
808
|
-
name[i] = toupper(name[i]);
|
809
|
-
}
|
810
|
-
params.set(name, it->second);
|
811
|
-
}
|
812
|
-
} else {
|
813
|
-
params.set("TITLE", "Internal server error");
|
814
|
-
}
|
815
|
-
string content = Template::apply(readAll(generalErrorFile), params);
|
816
|
-
params.set("CONTENT", content);
|
817
|
-
data = Template::apply(readAll(errorLayoutFile), params);
|
804
|
+
data = renderer.renderWithDetails(message, client->options, e);
|
818
805
|
} catch (const SystemException &e2) {
|
819
806
|
P_ERROR("Cannot render an error page: " << e2.what() << "\n" <<
|
820
807
|
e2.backtrace());
|
@@ -822,13 +809,7 @@ private:
|
|
822
809
|
}
|
823
810
|
} else {
|
824
811
|
try {
|
825
|
-
|
826
|
-
params.set("PROGRAM_NAME", PROGRAM_NAME);
|
827
|
-
params.set("NGINX_DOC_URL", NGINX_DOC_URL);
|
828
|
-
params.set("APACHE2_DOC_URL", APACHE2_DOC_URL);
|
829
|
-
params.set("STANDALONE_DOC_URL", STANDALONE_DOC_URL);
|
830
|
-
data = Template::apply(readAll(templatesDir + "/undisclosed_error.html.template"),
|
831
|
-
params);
|
812
|
+
data = renderer.renderWithoutDetails();
|
832
813
|
} catch (const SystemException &e2) {
|
833
814
|
P_ERROR("Cannot render an error page: " << e2.what() << "\n" <<
|
834
815
|
e2.backtrace());
|
@@ -1121,6 +1102,20 @@ private:
|
|
1121
1102
|
RH_TRACE(client, 3, "Response with chunked transfer encoding detected.");
|
1122
1103
|
client->chunkedResponse = true;
|
1123
1104
|
removeHeader(headerData, transferEncoding);
|
1105
|
+
} else {
|
1106
|
+
Header contentLength = lookupHeader(headerData, "Content-Length", "content-length");
|
1107
|
+
if (!contentLength.empty()) {
|
1108
|
+
client->responseContentLength = stringToLL(contentLength.value);
|
1109
|
+
}
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
Header connection = lookupHeader(headerData, "Connection", "connection");
|
1113
|
+
if (!connection.empty() && (connection.value == "keep-alive"
|
1114
|
+
|| connection.value == "Keep-Alive"))
|
1115
|
+
{
|
1116
|
+
RH_TRACE(client, 3, "Keep-alive response detected. Changing to non-keep alive.");
|
1117
|
+
removeHeader(headerData, connection);
|
1118
|
+
headerData.append("Connection: close\r\n");
|
1124
1119
|
}
|
1125
1120
|
|
1126
1121
|
// Add X-Powered-By.
|
@@ -1132,6 +1127,11 @@ private:
|
|
1132
1127
|
|
1133
1128
|
// Add sticky session ID.
|
1134
1129
|
if (client->stickySession && client->session != NULL) {
|
1130
|
+
StaticString baseURI = client->scgiParser.getHeader("SCRIPT_NAME");
|
1131
|
+
if (baseURI.empty()) {
|
1132
|
+
baseURI = StaticString("/", 1);
|
1133
|
+
}
|
1134
|
+
|
1135
1135
|
StaticString cookieName = getStickySessionCookieName(client);
|
1136
1136
|
// Note that we do NOT set HttpOnly. If we set that flag then Chrome
|
1137
1137
|
// doesn't send cookies over WebSocket handshakes. Confirmed on Chrome 25.
|
@@ -1139,6 +1139,8 @@ private:
|
|
1139
1139
|
headerData.append(cookieName.data(), cookieName.size());
|
1140
1140
|
headerData.append("=");
|
1141
1141
|
headerData.append(toString(client->session->getStickySessionId()));
|
1142
|
+
headerData.append("; Path=");
|
1143
|
+
headerData.append(baseURI.data(), baseURI.size());
|
1142
1144
|
headerData.append("\r\n");
|
1143
1145
|
|
1144
1146
|
// Invalidate all cookies with a different route.
|
@@ -1160,7 +1162,9 @@ private:
|
|
1160
1162
|
headerData.append(cookie.first.data(), cookie.first.size());
|
1161
1163
|
headerData.append("=");
|
1162
1164
|
headerData.append(cookie.second.data(), cookie.second.size());
|
1163
|
-
headerData.append(";
|
1165
|
+
headerData.append("; Path=");
|
1166
|
+
headerData.append(baseURI.data(), baseURI.size());
|
1167
|
+
headerData.append("; Expires=Thu, 01 Jan 1970 00:00:00 GMT\r\n");
|
1164
1168
|
}
|
1165
1169
|
}
|
1166
1170
|
}
|
@@ -1191,6 +1195,7 @@ private:
|
|
1191
1195
|
removeHeader(headerData, oobw);
|
1192
1196
|
}
|
1193
1197
|
|
1198
|
+
P_TRACE(2, "Fowarding response header from app client: " << headerData);
|
1194
1199
|
headerData.append("\r\n");
|
1195
1200
|
writeToClientOutputPipe(client, headerData);
|
1196
1201
|
return true;
|
@@ -1234,6 +1239,10 @@ private:
|
|
1234
1239
|
client->responseHeaderSeen = true;
|
1235
1240
|
StaticString header = client->responseHeaderBufferer.getData();
|
1236
1241
|
if (processResponseHeader(client, header)) {
|
1242
|
+
if (client->responseContentLength == 0) {
|
1243
|
+
RH_TRACE(client, 3, "Disconnecting client because response Content-Length = 0");
|
1244
|
+
onAppInputEof(client);
|
1245
|
+
}
|
1237
1246
|
return consumed;
|
1238
1247
|
} else {
|
1239
1248
|
assert(!client->connected());
|
@@ -1258,7 +1267,40 @@ private:
|
|
1258
1267
|
|
1259
1268
|
void onAppInputChunk(const ClientPtr &client, const StaticString &data) {
|
1260
1269
|
RH_LOG_EVENT(client, "onAppInputChunk");
|
1261
|
-
|
1270
|
+
StaticString data2;
|
1271
|
+
|
1272
|
+
if (client->responseContentLength == -1) {
|
1273
|
+
data2 = data;
|
1274
|
+
} else {
|
1275
|
+
size_t rest = client->responseContentLength -
|
1276
|
+
client->responseBodyAlreadyRead;
|
1277
|
+
data2 = StaticString(data.data(), std::min<size_t>(
|
1278
|
+
rest, data.size()));
|
1279
|
+
}
|
1280
|
+
|
1281
|
+
client->responseBodyAlreadyRead += data2.size();
|
1282
|
+
assert(client->responseContentLength == -1 || client->responseBodyAlreadyRead <=
|
1283
|
+
(unsigned long long) client->responseContentLength);
|
1284
|
+
if (data2.empty()) {
|
1285
|
+
// Client sent more data than was advertised through
|
1286
|
+
// Content-Length. Ignore them.
|
1287
|
+
return;
|
1288
|
+
}
|
1289
|
+
|
1290
|
+
writeToClientOutputPipe(client, data2);
|
1291
|
+
|
1292
|
+
if (client->responseContentLength > 0) {
|
1293
|
+
RH_TRACE(client, 3, client->responseBodyAlreadyRead << "/" <<
|
1294
|
+
client->responseContentLength <<
|
1295
|
+
" bytes of application data forwarded so far.");
|
1296
|
+
|
1297
|
+
if (client->connected() && (unsigned long long) client->responseContentLength
|
1298
|
+
== client->responseBodyAlreadyRead)
|
1299
|
+
{
|
1300
|
+
RH_TRACE(client, 3, "Disconnecting client because application data has been fully forwarded.");
|
1301
|
+
onAppInputEof(client);
|
1302
|
+
}
|
1303
|
+
}
|
1262
1304
|
}
|
1263
1305
|
|
1264
1306
|
void onAppInputChunkEnd(const ClientPtr &client) {
|
@@ -1270,11 +1312,15 @@ private:
|
|
1270
1312
|
RH_LOG_EVENT(client, "onAppInputEof");
|
1271
1313
|
// Check for session == NULL in order to avoid executing the code twice on
|
1272
1314
|
// responses with chunked encoding.
|
1315
|
+
// This also ensures that when onAppInputEof() is called twice (e.g. because
|
1316
|
+
// additional data was received after onAppInputChunk has already called onAppInputEof()),
|
1317
|
+
// we don't do things twice.
|
1273
1318
|
if (!client->connected() || client->session == NULL) {
|
1274
1319
|
return;
|
1275
1320
|
}
|
1276
1321
|
|
1277
1322
|
RH_DEBUG(client, "Application sent EOF");
|
1323
|
+
client->appInput->stop();
|
1278
1324
|
client->session.reset();
|
1279
1325
|
client->endScopeLog(&client->scopeLogs.requestProxying);
|
1280
1326
|
client->clientOutputPipe->end();
|
@@ -2254,14 +2300,10 @@ private:
|
|
2254
2300
|
}
|
2255
2301
|
|
2256
2302
|
void writeSpawnExceptionErrorResponse(const ClientPtr &client, const boost::shared_ptr<SpawnException> &e) {
|
2257
|
-
|
2258
|
-
|
2259
|
-
|
2260
|
-
|
2261
|
-
RH_WARN(client, "Cannot checkout session.\nError page:\n" <<
|
2262
|
-
e->getErrorPage());
|
2263
|
-
writeErrorResponse(client, e->getErrorPage(), e.get());
|
2264
|
-
}
|
2303
|
+
RH_ERROR(client, "Cannot checkout session because a spawning error occurred. " <<
|
2304
|
+
"The identifier of the error is " << e->get("ERROR_ID") << ". Please see earlier logs for " <<
|
2305
|
+
"details about the error.");
|
2306
|
+
writeErrorResponse(client, e->getErrorPage(), e.get());
|
2265
2307
|
}
|
2266
2308
|
|
2267
2309
|
void writeOtherExceptionErrorResponse(const ClientPtr &client, const ExceptionPtr &e) {
|
@@ -2418,7 +2460,9 @@ private:
|
|
2418
2460
|
}
|
2419
2461
|
|
2420
2462
|
StaticString connection = parser.getHeader("HTTP_CONNECTION");
|
2421
|
-
if (connection
|
2463
|
+
if (regex_match(connection.data(), connection.data() + connection.size(),
|
2464
|
+
upgradeHeaderRegex))
|
2465
|
+
{
|
2422
2466
|
data.append("Connection: ");
|
2423
2467
|
data.append(connection.data(), connection.size());
|
2424
2468
|
data.append("\r\n");
|
@@ -2440,12 +2484,25 @@ private:
|
|
2440
2484
|
data.append("\r\n");
|
2441
2485
|
}
|
2442
2486
|
|
2487
|
+
header = parser.getHeader("HTTPS");
|
2488
|
+
if (!header.empty()) {
|
2489
|
+
data.append("X-Forwarded-Proto: https\r\n");
|
2490
|
+
}
|
2491
|
+
|
2492
|
+
header = parser.getHeader("REMOTE_ADDR");
|
2493
|
+
if (!header.empty()) {
|
2494
|
+
data.append("X-Forwarded-For: ");
|
2495
|
+
data.append(header);
|
2496
|
+
data.append("\r\n");
|
2497
|
+
}
|
2498
|
+
|
2443
2499
|
if (client->options.analytics) {
|
2444
2500
|
data.append("Passenger-Txn-Id: ");
|
2445
2501
|
data.append(client->options.transaction->getTxnId());
|
2446
2502
|
data.append("\r\n");
|
2447
2503
|
}
|
2448
2504
|
|
2505
|
+
P_TRACE(3, "Sending headers to application: " << data);
|
2449
2506
|
data.append("\r\n");
|
2450
2507
|
|
2451
2508
|
StaticString datas[] = { data };
|
@@ -2650,11 +2707,13 @@ public:
|
|
2650
2707
|
pool(_pool),
|
2651
2708
|
options(_options),
|
2652
2709
|
resourceLocator(_options.passengerRoot),
|
2710
|
+
upgradeHeaderRegex("(keep-alive, *)?upgrade(, *keep-alive)?",
|
2711
|
+
boost::regex::perl | boost::regex::icase),
|
2653
2712
|
benchmarkPoint(getDefaultBenchmarkPoint())
|
2654
2713
|
{
|
2655
2714
|
accept4Available = true;
|
2656
2715
|
connectPasswordTimeout = 15000;
|
2657
|
-
unionStationCore = pool->
|
2716
|
+
unionStationCore = pool->getUnionStationCore();
|
2658
2717
|
|
2659
2718
|
requestSocketWatcher.set(_requestSocket, ev::READ);
|
2660
2719
|
requestSocketWatcher.set(_libev->getLoop());
|