passenger 4.0.44 → 4.0.45
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/.travis.yml +3 -0
- data/CHANGELOG +31 -0
- data/CONTRIBUTING.md +70 -10
- data/CONTRIBUTORS +4 -0
- data/README.md +1 -1
- data/Vagrantfile +50 -0
- data/bin/passenger-install-nginx-module +7 -2
- data/build/basics.rb +4 -1
- data/build/documentation.rb +6 -0
- data/build/node_tests.rb +7 -1
- data/build/packaging.rb +5 -0
- data/build/test_basics.rb +3 -3
- data/debian.template/copyright +1 -1
- data/debian.template/passenger.manpages +0 -1
- data/dev/rack.test/config.ru +5 -0
- data/dev/rack.test/public/asset.txt +1 -0
- data/dev/vagrant/apache_default_site.conf +35 -0
- data/dev/vagrant/apache_passenger.conf +5 -0
- data/dev/vagrant/apache_passenger.load +1 -0
- data/dev/vagrant/apache_ports.conf +24 -0
- data/dev/vagrant/apache_rack_test.conf +9 -0
- data/dev/vagrant/bashrc +21 -0
- data/dev/vagrant/nginx.conf +39 -0
- data/dev/vagrant/nginx_rakefile +34 -0
- data/dev/vagrant/nginx_start +32 -0
- data/dev/vagrant/provision.sh +115 -0
- data/dev/vagrant/sudoers.conf +5 -0
- data/doc/Design and Architecture.txt +515 -0
- data/doc/DeveloperQuickstart.md +70 -0
- data/doc/Users guide Apache.idmap.txt +24 -18
- data/doc/Users guide Apache.txt +200 -62
- data/doc/Users guide Nginx.idmap.txt +53 -45
- data/doc/Users guide Nginx.txt +501 -360
- data/doc/Users guide Standalone.txt +8 -0
- data/doc/images/direct_spawning.png +0 -0
- data/doc/images/direct_spawning.svg +16 -13
- data/doc/images/helper_agent_core_architecture.png +0 -0
- data/doc/images/passenger_architecture_overview.png +0 -0
- data/doc/images/smart_spawning.png +0 -0
- data/doc/images/{smart.svg → smart_spawning.svg} +23 -20
- data/doc/images/spawning_preparation_work.png +0 -0
- data/doc/images/startup_sequence.png +0 -0
- data/doc/users_guide_snippets/appendix_c_spawning_methods.txt +82 -121
- data/doc/users_guide_snippets/environment_variables.txt +1 -1
- data/doc/users_guide_snippets/support_information.txt +2 -0
- data/doc/users_guide_snippets/tips.txt +117 -9
- data/ext/apache2/Configuration.hpp +4 -2
- data/ext/apache2/ConfigurationCommands.cpp +14 -0
- data/ext/apache2/ConfigurationFields.hpp +4 -0
- data/ext/apache2/ConfigurationSetters.cpp +22 -0
- data/ext/apache2/CreateDirConfig.cpp +2 -0
- data/ext/apache2/Hooks.cpp +30 -14
- data/ext/apache2/MergeDirConfig.cpp +14 -0
- data/ext/apache2/SetHeaders.cpp +8 -0
- data/ext/common/ApplicationPool2/AppTypes.cpp +6 -1
- data/ext/common/ApplicationPool2/Implementation.cpp +1 -1
- data/ext/common/ApplicationPool2/Session.h +1 -1
- data/ext/common/Constants.h +9 -7
- data/ext/common/Utils/HttpHeaderBufferer.h +23 -4
- data/ext/common/Utils/StrIntUtils.h +35 -0
- data/ext/common/Utils/StringScanning.h +4 -10
- data/ext/common/agents/HelperAgent/RequestHandler.h +90 -49
- data/ext/nginx/CacheLocationConfig.c +40 -0
- data/ext/nginx/ConfigurationCommands.c +20 -0
- data/ext/nginx/ConfigurationFields.h +4 -0
- data/ext/nginx/ContentHandler.c +1 -1
- data/ext/nginx/CreateLocationConfig.c +9 -0
- data/ext/nginx/MergeLocationConfig.c +12 -0
- data/ext/nginx/config +2 -2
- data/ext/nginx/ngx_http_passenger_module.c +4 -4
- data/helper-scripts/node-loader.js +40 -27
- data/lib/phusion_passenger.rb +1 -1
- data/lib/phusion_passenger/apache2/config_options.rb +14 -2
- data/lib/phusion_passenger/constants.rb +7 -6
- data/lib/phusion_passenger/loader_shared_helpers.rb +11 -1
- data/lib/phusion_passenger/nginx/config_options.rb +8 -0
- data/lib/phusion_passenger/packaging.rb +8 -3
- data/lib/phusion_passenger/platform_info/apache.rb +3 -0
- data/lib/phusion_passenger/platform_info/ruby.rb +4 -1
- data/lib/phusion_passenger/standalone/command.rb +0 -1
- data/lib/phusion_passenger/standalone/package_runtime_command.rb +1 -0
- data/lib/phusion_passenger/standalone/start_command.rb +80 -62
- data/lib/phusion_passenger/standalone/status_command.rb +1 -0
- data/lib/phusion_passenger/standalone/stop_command.rb +1 -0
- data/man/passenger-config.1 +1 -1
- data/man/passenger-memory-stats.8 +1 -1
- data/man/passenger-status.8 +1 -1
- data/npm-shrinkwrap.json +229 -0
- data/package.json +28 -0
- data/resources/templates/standalone/config.erb +2 -0
- data/rpm/Vagrantfile +0 -3
- data/test/config.json.vagrant +30 -0
- data/test/cxx/HttpHeaderBuffererTest.cpp +64 -10
- data/test/cxx/RequestHandlerTest.cpp +35 -13
- data/test/integration_tests/apache2_tests.rb +1 -0
- data/test/stub/node/app.js +26 -18
- metadata +28 -13
- metadata.gz.asc +7 -7
- data/doc/Architectural overview.idmap.txt +0 -36
- data/doc/Architectural overview.txt +0 -410
- data/doc/images/smart.png +0 -0
- data/ext/common/ApplicationPool2/README.md +0 -56
- data/man/passenger-stress-test.1 +0 -43
- data/node_lib/phusion_passenger/httplib_emulation.js +0 -215
- data/node_lib/phusion_passenger/request_handler.js +0 -73
- data/node_lib/phusion_passenger/session_protocol_parser.js +0 -113
- data/test/node/httplib_emulation_spec.js +0 -623
@@ -30,7 +30,12 @@
|
|
30
30
|
namespace Passenger {
|
31
31
|
namespace ApplicationPool2 {
|
32
32
|
|
33
|
-
|
33
|
+
/* If you update this structure, also update the following:
|
34
|
+
* - ApplicationPool2::Options::getStartCommand()
|
35
|
+
* - lib/phusion_passenger/standalone/app_finder.rb
|
36
|
+
* - The documentation for `PassengerAppEnv` (Apache) and `passenger_app_env` (Nginx)
|
37
|
+
* - The Developer Guide, section "Executing the loader or preloader"
|
38
|
+
*/
|
34
39
|
const AppTypeDefinition appTypeDefinitions[] = {
|
35
40
|
{ PAT_RACK, "rack", "config.ru", "Passenger RackApp" },
|
36
41
|
{ PAT_WSGI, "wsgi", "passenger_wsgi.py", "Passenger WsgiApp" },
|
@@ -108,7 +108,7 @@ public:
|
|
108
108
|
const string &getConnectPassword() const;
|
109
109
|
pid_t getPid() const;
|
110
110
|
const string &getGupid() const;
|
111
|
-
int getStickySessionId() const;
|
111
|
+
unsigned int getStickySessionId() const;
|
112
112
|
const GroupPtr getGroup() const;
|
113
113
|
void requestOOBW();
|
114
114
|
int kill(int signo);
|
data/ext/common/Constants.h
CHANGED
@@ -36,7 +36,7 @@
|
|
36
36
|
#define DEFAULT_BACKEND_ACCOUNT_RIGHTS Account::DETACH
|
37
37
|
|
38
38
|
|
39
|
-
#define APACHE2_DOC_URL "
|
39
|
+
#define APACHE2_DOC_URL "https://www.phusionpassenger.com/documentation/Users%20guide%20Apache.html"
|
40
40
|
|
41
41
|
#define DEB_APACHE_MODULE_PACKAGE "libapache2-mod-passenger"
|
42
42
|
|
@@ -68,6 +68,8 @@
|
|
68
68
|
|
69
69
|
#define DEFAULT_START_TIMEOUT 90000
|
70
70
|
|
71
|
+
#define DEFAULT_STICKY_SESSIONS_COOKIE_NAME "_passenger_route"
|
72
|
+
|
71
73
|
#define DEFAULT_THREAD_COUNT 1
|
72
74
|
|
73
75
|
#define DEFAULT_UNION_STATION_GATEWAY_ADDRESS "gateway.unionstationapp.com"
|
@@ -76,19 +78,19 @@
|
|
76
78
|
|
77
79
|
#define DEFAULT_WEB_APP_USER "nobody"
|
78
80
|
|
79
|
-
#define ENTERPRISE_URL "
|
81
|
+
#define ENTERPRISE_URL "https://www.phusionpassenger.com/enterprise"
|
80
82
|
|
81
83
|
#define FEEDBACK_FD 3
|
82
84
|
|
83
|
-
#define INDEX_DOC_URL "
|
85
|
+
#define INDEX_DOC_URL "https://www.phusionpassenger.com/documentation/Users%20guide.html"
|
84
86
|
|
85
87
|
#define MESSAGE_SERVER_MAX_PASSWORD_SIZE 100
|
86
88
|
|
87
89
|
#define MESSAGE_SERVER_MAX_USERNAME_SIZE 100
|
88
90
|
|
89
|
-
#define NGINX_DOC_URL "
|
91
|
+
#define NGINX_DOC_URL "https://www.phusionpassenger.com/documentation/Users%20guide%20Nginx.html"
|
90
92
|
|
91
|
-
#define PASSENGER_VERSION "4.0.
|
93
|
+
#define PASSENGER_VERSION "4.0.45"
|
92
94
|
|
93
95
|
#define POOL_HELPER_THREAD_STACK_SIZE 262144
|
94
96
|
|
@@ -114,11 +116,11 @@
|
|
114
116
|
|
115
117
|
#define SERVER_INSTANCE_DIR_STRUCTURE_MINOR_VERSION 0
|
116
118
|
|
117
|
-
#define STANDALONE_DOC_URL "
|
119
|
+
#define STANDALONE_DOC_URL "https://www.phusionpassenger.com/documentation/Users%20guide%20Standalone.html"
|
118
120
|
|
119
121
|
#define STANDALONE_NGINX_CONFIGURE_OPTIONS "--with-cc-opt='-Wno-error' --without-http_fastcgi_module --without-http_scgi_module --without-http_uwsgi_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_ssl_module"
|
120
122
|
|
121
|
-
#define SUPPORT_URL "
|
123
|
+
#define SUPPORT_URL "https://www.phusionpassenger.com/documentation_and_support"
|
122
124
|
|
123
125
|
|
124
126
|
#endif /* _PASSENGER_CONSTANTS_H */
|
@@ -28,6 +28,7 @@
|
|
28
28
|
#include <string>
|
29
29
|
#include <algorithm>
|
30
30
|
#include <cstddef>
|
31
|
+
#include <cstring>
|
31
32
|
#include <cassert>
|
32
33
|
#include <StaticString.h>
|
33
34
|
#include <Utils/StreamBoyerMooreHorspool.h>
|
@@ -41,7 +42,8 @@ using namespace std;
|
|
41
42
|
*
|
42
43
|
* Feed data until acceptingInput() is false. The entire HTTP header
|
43
44
|
* will become available through getData(). Non-HTTP header data is
|
44
|
-
* not consumed and will not be included in getData().
|
45
|
+
* not consumed and will not be included in getData(). 100-Continue
|
46
|
+
* messages are ignored.
|
45
47
|
*
|
46
48
|
* This class has zero-copy support. If the first feed already contains
|
47
49
|
* an entire HTTP header getData() will point to the fed data. Otherwise
|
@@ -77,6 +79,13 @@ private:
|
|
77
79
|
char padding[SBMH_SIZE(4)];
|
78
80
|
} u;
|
79
81
|
|
82
|
+
bool is100Continue(const StaticString &buffer) const {
|
83
|
+
return buffer.size() >= sizeof("HTTP/1.1 100 Continue\r\n") - 1
|
84
|
+
&& memcmp(buffer.data(), "HTTP/1.", sizeof("HTTP/1.") - 1) == 0
|
85
|
+
&& memcmp(buffer.data() + sizeof("HTTP/1.1 ") - 1,
|
86
|
+
"100 Continue\r\n", sizeof("100 Continue\r\n") - 1) == 0;
|
87
|
+
}
|
88
|
+
|
80
89
|
public:
|
81
90
|
HttpHeaderBufferer() {
|
82
91
|
sbmh_init(&u.terminatorFinder,
|
@@ -114,8 +123,13 @@ public:
|
|
114
123
|
(const unsigned char *) data,
|
115
124
|
feedSize);
|
116
125
|
if (u.terminatorFinder.found) {
|
117
|
-
|
118
|
-
|
126
|
+
if (is100Continue(StaticString(data, accepted))) {
|
127
|
+
reset();
|
128
|
+
accepted += feed(data + accepted, size - accepted);
|
129
|
+
} else {
|
130
|
+
state = DONE;
|
131
|
+
this->data = StaticString(data, accepted);
|
132
|
+
}
|
119
133
|
} else if (feedSize == max) {
|
120
134
|
state = ERROR;
|
121
135
|
this->data = StaticString(data, accepted);
|
@@ -135,7 +149,12 @@ public:
|
|
135
149
|
buffer.append(data, accepted);
|
136
150
|
this->data = buffer;
|
137
151
|
if (u.terminatorFinder.found) {
|
138
|
-
|
152
|
+
if (is100Continue(buffer)) {
|
153
|
+
reset();
|
154
|
+
accepted += feed(data + accepted, size - accepted);
|
155
|
+
} else {
|
156
|
+
state = DONE;
|
157
|
+
}
|
139
158
|
} else if (buffer.size() == (size_t) max) {
|
140
159
|
state = ERROR;
|
141
160
|
}
|
@@ -144,6 +144,41 @@ string replaceAll(const string &str, const string &toFind, const string &replace
|
|
144
144
|
*/
|
145
145
|
string strip(const StaticString &str);
|
146
146
|
|
147
|
+
/**
|
148
|
+
* Given a pointer to a NULL-terminated string, update it to a
|
149
|
+
* position where all leading whitespaces (0x20) have been skipped.
|
150
|
+
*/
|
151
|
+
inline void
|
152
|
+
skipLeadingWhitespaces(const char **data) {
|
153
|
+
while (**data == ' ') {
|
154
|
+
(*data)++;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
/**
|
159
|
+
* Given a pointer to a string and its end, update the begin pointer to a
|
160
|
+
* position where all leading whitespaces (0x20) have been skipped.
|
161
|
+
* The pointer will not be moved past `end`.
|
162
|
+
*/
|
163
|
+
inline void
|
164
|
+
skipLeadingWhitespaces(const char **data, const char *end) {
|
165
|
+
while (*data < end && **data == ' ') {
|
166
|
+
(*data)++;
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
/**
|
171
|
+
* Given a string and a pointer to its position within it, update the pointer
|
172
|
+
* to a position where all trailing whitespaces (0x20) have been skipped.
|
173
|
+
* The pointer will not be moved before `begin`.
|
174
|
+
*/
|
175
|
+
inline void
|
176
|
+
skipTrailingWhitespaces(const char *begin, const char **pos) {
|
177
|
+
while (*pos > begin && (*pos)[-1] == ' ') {
|
178
|
+
(*pos)--;
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
147
182
|
/**
|
148
183
|
* Convert anything to a string.
|
149
184
|
*/
|
@@ -29,6 +29,7 @@
|
|
29
29
|
#include <cstdlib>
|
30
30
|
#include <string>
|
31
31
|
#include <StaticString.h>
|
32
|
+
#include <Utils/StrIntUtils.h>
|
32
33
|
|
33
34
|
|
34
35
|
/**
|
@@ -58,13 +59,6 @@ using namespace std;
|
|
58
59
|
|
59
60
|
struct ParseException {};
|
60
61
|
|
61
|
-
inline void
|
62
|
-
_skipLeadingWhitespaces(const char **data) {
|
63
|
-
while (**data == ' ') {
|
64
|
-
(*data)++;
|
65
|
-
}
|
66
|
-
}
|
67
|
-
|
68
62
|
/**
|
69
63
|
* Scan the given data for the first word that appears on the first line.
|
70
64
|
* Leading whitespaces (but not newlines) are ignored. If a word is found
|
@@ -78,7 +72,7 @@ _skipLeadingWhitespaces(const char **data) {
|
|
78
72
|
*/
|
79
73
|
inline StaticString
|
80
74
|
readNextWord(const char **data) {
|
81
|
-
|
75
|
+
skipLeadingWhitespaces(data);
|
82
76
|
if (**data == '\n' || **data == '\0') {
|
83
77
|
throw ParseException();
|
84
78
|
}
|
@@ -201,7 +195,7 @@ readNextWordAsDouble(const char **data) {
|
|
201
195
|
*/
|
202
196
|
inline string
|
203
197
|
readRestOfLine(const char *data) {
|
204
|
-
|
198
|
+
skipLeadingWhitespaces(&data);
|
205
199
|
// Rest of line is allowed to be empty.
|
206
200
|
if (*data == '\n' || *data == '\0') {
|
207
201
|
return "";
|
@@ -251,7 +245,7 @@ skipToNextLine(const char **data) {
|
|
251
245
|
*/
|
252
246
|
inline StaticString
|
253
247
|
readNextSentence(const char **data, char terminator) {
|
254
|
-
|
248
|
+
skipLeadingWhitespaces(data);
|
255
249
|
if (**data == '\n' || **data == '\0' || **data == terminator) {
|
256
250
|
throw ParseException();
|
257
251
|
}
|
@@ -85,24 +85,22 @@
|
|
85
85
|
present? header present? protocol
|
86
86
|
---------------------------------------------------------------------------------------------
|
87
87
|
|
88
|
-
GET/HEAD
|
88
|
+
GET/HEAD Y Y - Reject request[1]
|
89
89
|
Other Y - - Reject request[2]
|
90
90
|
|
91
|
-
- N N http_session Set requestBodyLength=0, keep socket open when done forwarding.
|
92
91
|
GET/HEAD Y N http_session Set requestBodyLength=-1, keep socket open when done forwarding.
|
93
|
-
|
92
|
+
- N N http_session Set requestBodyLength=0, keep socket open when done forwarding.
|
93
|
+
- N Y http_session Keep socket open when done forwarding. If Transfer-Encoding is
|
94
94
|
chunked, rechunck the body during forwarding.
|
95
95
|
|
96
|
-
- N N session Set requestBodyLength=0, half-close app socket when done forwarding.
|
97
96
|
GET/HEAD Y N session Set requestBodyLength=-1, half-close app socket when done forwarding.
|
98
|
-
|
97
|
+
- N N session Set requestBodyLength=0, half-close app socket when done forwarding.
|
98
|
+
- N Y session Half-close app socket when done forwarding.
|
99
99
|
---------------------------------------------------------------------------------------------
|
100
100
|
|
101
101
|
[1] Supporting situations in which there is both an HTTP request body and WebSocket data
|
102
|
-
is way too complicated. The RequestHandler code is complicated enough as it is
|
103
|
-
|
104
|
-
support by other servers are shaky at best. For these reasons, we don't bother
|
105
|
-
supporting GET requests with body at all.
|
102
|
+
is way too complicated. The RequestHandler code is complicated enough as it is,
|
103
|
+
so we choose not to support requests like these.
|
106
104
|
[2] RFC 6455 states that WebSocket upgrades may only happen over GET requests.
|
107
105
|
We don't bother supporting non-WebSocket upgrades.
|
108
106
|
|
@@ -1135,11 +1133,37 @@ private:
|
|
1135
1133
|
// Add sticky session ID.
|
1136
1134
|
if (client->stickySession && client->session != NULL) {
|
1137
1135
|
StaticString cookieName = getStickySessionCookieName(client);
|
1136
|
+
// Note that we do NOT set HttpOnly. If we set that flag then Chrome
|
1137
|
+
// doesn't send cookies over WebSocket handshakes. Confirmed on Chrome 25.
|
1138
1138
|
headerData.append("Set-Cookie: ");
|
1139
1139
|
headerData.append(cookieName.data(), cookieName.size());
|
1140
1140
|
headerData.append("=");
|
1141
1141
|
headerData.append(toString(client->session->getStickySessionId()));
|
1142
|
-
headerData.append("
|
1142
|
+
headerData.append("\r\n");
|
1143
|
+
|
1144
|
+
// Invalidate all cookies with a different route.
|
1145
|
+
//
|
1146
|
+
// TODO: This is not entirely correct. Clients MAY send multiple Cookie
|
1147
|
+
// headers, although this is in practice extremely rare.
|
1148
|
+
// http://stackoverflow.com/questions/16305814/are-multiple-cookie-headers-allowed-in-an-http-request
|
1149
|
+
StaticString cookieHeader = client->scgiParser.getHeader("HTTP_COOKIE");
|
1150
|
+
vector< pair<StaticString, StaticString> > cookies;
|
1151
|
+
pair<StaticString, StaticString> cookie;
|
1152
|
+
|
1153
|
+
parseCookieHeader(cookieHeader, cookies);
|
1154
|
+
|
1155
|
+
foreach (cookie, cookies) {
|
1156
|
+
if (cookie.first == cookieName) {
|
1157
|
+
unsigned int stickySessionId = stringToUint(cookie.second);
|
1158
|
+
if (stickySessionId != client->session->getStickySessionId()) {
|
1159
|
+
headerData.append("Set-Cookie: ");
|
1160
|
+
headerData.append(cookie.first.data(), cookie.first.size());
|
1161
|
+
headerData.append("=");
|
1162
|
+
headerData.append(cookie.second.data(), cookie.second.size());
|
1163
|
+
headerData.append("; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT\r\n");
|
1164
|
+
}
|
1165
|
+
}
|
1166
|
+
}
|
1143
1167
|
}
|
1144
1168
|
|
1145
1169
|
// Add Date header. https://code.google.com/p/phusion-passenger/issues/detail?id=485
|
@@ -1206,6 +1230,7 @@ private:
|
|
1206
1230
|
disconnectWithError(client, "application response format error (invalid header)");
|
1207
1231
|
} else {
|
1208
1232
|
// Now that we have a full header, do something with it.
|
1233
|
+
RH_TRACE(client, 3, "Response header fully buffered");
|
1209
1234
|
client->responseHeaderSeen = true;
|
1210
1235
|
StaticString header = client->responseHeaderBufferer.getData();
|
1211
1236
|
if (processResponseHeader(client, header)) {
|
@@ -1776,18 +1801,10 @@ private:
|
|
1776
1801
|
const bool requestIsGetOrHead = requestMethod == "GET" || requestMethod == "HEAD";
|
1777
1802
|
const bool requestBodyOffered = contentLength != -1 || !transferEncoding.empty();
|
1778
1803
|
|
1779
|
-
// Reject requests that have a request body
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1783
|
-
reportBadRequestAndDisconnect(client, "Bad request (GET and HEAD requests may not contain a request body)");
|
1784
|
-
return;
|
1785
|
-
}
|
1786
|
-
} else {
|
1787
|
-
if (!upgrade.empty()) {
|
1788
|
-
reportBadRequestAndDisconnect(client, "Bad request (Upgrade header is only allowed for non-GET and non-HEAD requests)");
|
1789
|
-
return;
|
1790
|
-
}
|
1804
|
+
// Reject requests that have a request body and an Upgrade header.
|
1805
|
+
if (!requestIsGetOrHead && !upgrade.empty()) {
|
1806
|
+
reportBadRequestAndDisconnect(client, "Bad request (Upgrade header is only allowed for non-GET and non-HEAD requests)");
|
1807
|
+
return;
|
1791
1808
|
}
|
1792
1809
|
|
1793
1810
|
if (!requestBodyOffered) {
|
@@ -1960,48 +1977,72 @@ private:
|
|
1960
1977
|
}
|
1961
1978
|
}
|
1962
1979
|
|
1980
|
+
void parseCookieHeader(const StaticString &header,
|
1981
|
+
vector< pair<StaticString, StaticString> > &cookies) const
|
1982
|
+
{
|
1983
|
+
// See http://stackoverflow.com/questions/6108207/definite-guide-to-valid-cookie-values
|
1984
|
+
// for syntax grammar.
|
1985
|
+
vector<StaticString> parts;
|
1986
|
+
vector<StaticString>::const_iterator it, it_end;
|
1987
|
+
|
1988
|
+
split(header, ';', parts);
|
1989
|
+
cookies.reserve(parts.size());
|
1990
|
+
it_end = parts.end();
|
1991
|
+
|
1992
|
+
for (it = parts.begin(); it != it_end; it++) {
|
1993
|
+
const char *begin = it->data();
|
1994
|
+
const char *end = it->data() + it->size();
|
1995
|
+
const char *sep;
|
1996
|
+
|
1997
|
+
skipLeadingWhitespaces(&begin, end);
|
1998
|
+
skipTrailingWhitespaces(begin, &end);
|
1999
|
+
|
2000
|
+
// Find the separator ('=').
|
2001
|
+
sep = (const char *) memchr(begin, '=', end - begin);
|
2002
|
+
if (sep != NULL) {
|
2003
|
+
// Valid cookie. Otherwise, ignore it.
|
2004
|
+
const char *nameEnd = sep;
|
2005
|
+
const char *valueBegin = sep + 1;
|
2006
|
+
|
2007
|
+
skipTrailingWhitespaces(begin, &nameEnd);
|
2008
|
+
skipLeadingWhitespaces(&valueBegin, end);
|
2009
|
+
|
2010
|
+
cookies.push_back(make_pair(
|
2011
|
+
StaticString(begin, nameEnd - begin),
|
2012
|
+
StaticString(valueBegin, end - valueBegin)
|
2013
|
+
));
|
2014
|
+
}
|
2015
|
+
}
|
2016
|
+
}
|
2017
|
+
|
1963
2018
|
void setStickySessionId(const ClientPtr &client) {
|
1964
2019
|
ScgiRequestParser &parser = client->scgiParser;
|
1965
|
-
if (parser.getHeader("
|
2020
|
+
if (parser.getHeader("PASSENGER_STICKY_SESSIONS") == "true") {
|
1966
2021
|
// TODO: This is not entirely correct. Clients MAY send multiple Cookie
|
1967
2022
|
// headers, although this is in practice extremely rare.
|
1968
2023
|
// http://stackoverflow.com/questions/16305814/are-multiple-cookie-headers-allowed-in-an-http-request
|
1969
|
-
StaticString
|
2024
|
+
StaticString cookieHeader = parser.getHeader("HTTP_COOKIE");
|
1970
2025
|
StaticString cookieName = getStickySessionCookieName(client);
|
1971
|
-
vector<StaticString>
|
2026
|
+
vector< pair<StaticString, StaticString> > cookies;
|
2027
|
+
pair<StaticString, StaticString> cookie;
|
1972
2028
|
|
1973
2029
|
client->stickySession = true;
|
1974
|
-
|
1975
|
-
foreach (
|
1976
|
-
|
1977
|
-
|
1978
|
-
|
1979
|
-
|
1980
|
-
// Skip leading whitespace in the name.
|
1981
|
-
while (begin < end && *begin == ' ') {
|
1982
|
-
begin++;
|
1983
|
-
}
|
1984
|
-
part = StaticString(begin, end - begin);
|
1985
|
-
|
1986
|
-
// Find the separator ('=').
|
1987
|
-
sep = (const char *) memchr(begin, '=', end - begin);
|
1988
|
-
if (sep != NULL) {
|
1989
|
-
StaticString name(begin, sep - begin);
|
1990
|
-
if (name == cookieName) {
|
1991
|
-
// This cookie matches the one we're looking for.
|
1992
|
-
StaticString value(sep + 1, end - (sep + 1));
|
1993
|
-
client->options.stickySessionId = stringToUint(value);
|
1994
|
-
return;
|
1995
|
-
}
|
2030
|
+
parseCookieHeader(cookieHeader, cookies);
|
2031
|
+
foreach (cookie, cookies) {
|
2032
|
+
if (cookie.first == cookieName) {
|
2033
|
+
// This cookie matches the one we're looking for.
|
2034
|
+
client->options.stickySessionId = stringToUint(cookie.second);
|
2035
|
+
return;
|
1996
2036
|
}
|
1997
2037
|
}
|
1998
2038
|
}
|
1999
2039
|
}
|
2000
2040
|
|
2001
2041
|
StaticString getStickySessionCookieName(const ClientPtr &client) const {
|
2002
|
-
StaticString value = client->scgiParser.getHeader("
|
2042
|
+
StaticString value = client->scgiParser.getHeader("PASSENGER_STICKY_SESSIONS_COOKIE_NAME");
|
2003
2043
|
if (value.empty()) {
|
2004
|
-
return StaticString(
|
2044
|
+
return StaticString(DEFAULT_STICKY_SESSIONS_COOKIE_NAME,
|
2045
|
+
sizeof(DEFAULT_STICKY_SESSIONS_COOKIE_NAME) - 1);
|
2005
2046
|
} else {
|
2006
2047
|
return value;
|
2007
2048
|
}
|
@@ -246,6 +246,20 @@ u_char int_buf[32], *end, *buf, *pos;
|
|
246
246
|
}
|
247
247
|
|
248
248
|
|
249
|
+
|
250
|
+
if (conf->sticky_sessions != NGX_CONF_UNSET) {
|
251
|
+
len += 26;
|
252
|
+
len += conf->sticky_sessions ? sizeof("true") : sizeof("false");
|
253
|
+
}
|
254
|
+
|
255
|
+
|
256
|
+
|
257
|
+
if (conf->sticky_sessions_cookie_name.data != NULL) {
|
258
|
+
len += 38;
|
259
|
+
len += conf->sticky_sessions_cookie_name.len + 1;
|
260
|
+
}
|
261
|
+
|
262
|
+
|
249
263
|
|
250
264
|
/* Create string */
|
251
265
|
buf = pos = ngx_pnalloc(cf->pool, len);
|
@@ -604,6 +618,32 @@ buf = pos = ngx_pnalloc(cf->pool, len);
|
|
604
618
|
}
|
605
619
|
|
606
620
|
|
621
|
+
|
622
|
+
if (conf->sticky_sessions != NGX_CONF_UNSET) {
|
623
|
+
pos = ngx_copy(pos,
|
624
|
+
"PASSENGER_STICKY_SESSIONS",
|
625
|
+
26);
|
626
|
+
if (conf->sticky_sessions) {
|
627
|
+
pos = ngx_copy(pos, "true", sizeof("true"));
|
628
|
+
} else {
|
629
|
+
pos = ngx_copy(pos, "false", sizeof("false"));
|
630
|
+
}
|
631
|
+
}
|
632
|
+
|
633
|
+
|
634
|
+
|
635
|
+
if (conf->sticky_sessions_cookie_name.data != NULL) {
|
636
|
+
pos = ngx_copy(pos,
|
637
|
+
"PASSENGER_STICKY_SESSIONS_COOKIE_NAME",
|
638
|
+
38);
|
639
|
+
pos = ngx_copy(pos,
|
640
|
+
conf->sticky_sessions_cookie_name.data,
|
641
|
+
conf->sticky_sessions_cookie_name.len);
|
642
|
+
*pos = '\0';
|
643
|
+
pos++;
|
644
|
+
}
|
645
|
+
|
646
|
+
|
607
647
|
|
608
648
|
conf->options_cache.data = buf;
|
609
649
|
conf->options_cache.len = pos - buf;
|