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
data/ext/apache2/Hooks.cpp
CHANGED
@@ -1224,6 +1224,7 @@ public:
|
|
1224
1224
|
.set ("default_ruby", serverConfig.defaultRuby)
|
1225
1225
|
.setInt ("max_pool_size", serverConfig.maxPoolSize)
|
1226
1226
|
.setInt ("pool_idle_time", serverConfig.poolIdleTime)
|
1227
|
+
.setInt ("response_buffer_high_watermark", serverConfig.responseBufferHighWatermark)
|
1227
1228
|
.setInt ("stat_throttle_rate", serverConfig.statThrottleRate)
|
1228
1229
|
.set ("analytics_log_user", serverConfig.analyticsLogUser)
|
1229
1230
|
.set ("analytics_log_group", serverConfig.analyticsLogGroup)
|
data/ext/common/Constants.h
CHANGED
@@ -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
|
*
|
@@ -78,6 +78,8 @@
|
|
78
78
|
|
79
79
|
#define DEFAULT_PYTHON "python"
|
80
80
|
|
81
|
+
#define DEFAULT_RESPONSE_BUFFER_HIGH_WATERMARK 134217728
|
82
|
+
|
81
83
|
#define DEFAULT_RUBY "ruby"
|
82
84
|
|
83
85
|
#define DEFAULT_SPAWN_METHOD "smart"
|
@@ -110,7 +112,7 @@
|
|
110
112
|
|
111
113
|
#define NGINX_DOC_URL "https://www.phusionpassenger.com/documentation/Users%20guide%20Nginx.html"
|
112
114
|
|
113
|
-
#define PASSENGER_VERSION "5.0.0.
|
115
|
+
#define PASSENGER_VERSION "5.0.0.beta3"
|
114
116
|
|
115
117
|
#define POOL_HELPER_THREAD_STACK_SIZE 262144
|
116
118
|
|
data/ext/common/Constants.h.erb
CHANGED
@@ -117,6 +117,16 @@ psg_lstr_append_part(LString *str, LString::Part *part) {
|
|
117
117
|
part->next = NULL;
|
118
118
|
}
|
119
119
|
|
120
|
+
inline void
|
121
|
+
psg_lstr_append_part_from_another_lstr(LString *str, psg_pool_t *pool, const LString::Part *part) {
|
122
|
+
LString::Part *copy = (LString::Part *) psg_palloc(pool, sizeof(LString::Part));
|
123
|
+
*copy = *part;
|
124
|
+
if (part->mbuf_block != NULL) {
|
125
|
+
mbuf_block_ref(part->mbuf_block);
|
126
|
+
}
|
127
|
+
psg_lstr_append_part(str, copy);
|
128
|
+
}
|
129
|
+
|
120
130
|
inline void
|
121
131
|
psg_lstr_append(LString *str, psg_pool_t *pool, const MemoryKit::mbuf &buffer,
|
122
132
|
const char *data, unsigned int size)
|
@@ -133,7 +133,7 @@ using namespace boost;
|
|
133
133
|
*
|
134
134
|
* ...process buffer....
|
135
135
|
*
|
136
|
-
* return Channel::Result(bytesProcessed, acceptFurtherData);
|
136
|
+
* return Channel::Result(bytesProcessed, !acceptFurtherData);
|
137
137
|
* } else if (errcode == 0) {
|
138
138
|
* // EOF reached. Result doesn't matter in this case.
|
139
139
|
* return Channel::Result(0, false);
|
@@ -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
|
*
|
@@ -33,6 +33,7 @@
|
|
33
33
|
#include <Constants.h>
|
34
34
|
#include <Utils/StrIntUtils.h>
|
35
35
|
#include <Utils/json.h>
|
36
|
+
#include <Utils/JsonUtils.h>
|
36
37
|
|
37
38
|
namespace Passenger {
|
38
39
|
namespace ServerKit {
|
@@ -61,26 +62,6 @@ private:
|
|
61
62
|
MemoryKit::mbuf_pool_init(&mbuf_pool);
|
62
63
|
}
|
63
64
|
|
64
|
-
string formatFloat(double val) const {
|
65
|
-
char buf[64];
|
66
|
-
int size = snprintf(buf, sizeof(buf), "%.1f", val);
|
67
|
-
return string(buf, size);
|
68
|
-
}
|
69
|
-
|
70
|
-
Json::Value
|
71
|
-
byteSizeToJson(size_t size) const {
|
72
|
-
Json::Value doc;
|
73
|
-
doc["bytes"] = (Json::UInt64) size;
|
74
|
-
if (size < 1024) {
|
75
|
-
doc["human_readable"] = toString(size) + " bytes";
|
76
|
-
} else if (size < 1024 * 1024) {
|
77
|
-
doc["human_readable"] = formatFloat(size / 1024.0) + " KB";
|
78
|
-
} else {
|
79
|
-
doc["human_readable"] = formatFloat(size / 1024.0 / 1024.0) + " MB";
|
80
|
-
}
|
81
|
-
return doc;
|
82
|
-
}
|
83
|
-
|
84
65
|
public:
|
85
66
|
SafeLibevPtr libev;
|
86
67
|
struct MemoryKit::mbuf_pool mbuf_pool;
|
@@ -0,0 +1,246 @@
|
|
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_SERVER_KIT_COOKIE_UTILS_H_
|
26
|
+
#define _PASSENGER_SERVER_KIT_COOKIE_UTILS_H_
|
27
|
+
|
28
|
+
#include <cstring>
|
29
|
+
#include <cassert>
|
30
|
+
#include <MemoryKit/palloc.h>
|
31
|
+
#include <DataStructures/LString.h>
|
32
|
+
|
33
|
+
namespace Passenger {
|
34
|
+
namespace ServerKit {
|
35
|
+
|
36
|
+
|
37
|
+
inline bool findCookieNameValueSeparator(const LString::Part *part, size_t index,
|
38
|
+
const LString::Part **separatorPart, size_t *separatorIndex);
|
39
|
+
inline bool findCookieEnd(const LString::Part *separatorPart, size_t separatorIndex,
|
40
|
+
const LString::Part **endPart, size_t *endIndex);
|
41
|
+
inline bool matchCookieName(psg_pool_t *pool, const LString::Part *part, size_t index,
|
42
|
+
const LString::Part *separatorPart, size_t separatorIndex,
|
43
|
+
const LString *name);
|
44
|
+
inline LString *extractCookieValue(psg_pool_t *pool,
|
45
|
+
const LString::Part *separatorPart, size_t separatorIndex,
|
46
|
+
const LString::Part *endPart, size_t endIndex);
|
47
|
+
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Given the value of an HTTP cookie header, returns the value of the cookie
|
51
|
+
* of the given name, or NULL if not found.
|
52
|
+
*/
|
53
|
+
inline LString *
|
54
|
+
findCookie(psg_pool_t *pool, const LString *cookieHeaderValue, const LString *name) {
|
55
|
+
const LString::Part *part = cookieHeaderValue->start;
|
56
|
+
const LString::Part *separatorPart, *endPart;
|
57
|
+
size_t index = 0, separatorIndex, endIndex;
|
58
|
+
bool done = part == NULL;
|
59
|
+
LString *result = NULL;
|
60
|
+
|
61
|
+
while (!done) {
|
62
|
+
if (findCookieNameValueSeparator(part, index, &separatorPart, &separatorIndex)) {
|
63
|
+
if (!findCookieEnd(separatorPart, separatorIndex, &endPart, &endIndex)) {
|
64
|
+
done = true;
|
65
|
+
} else if (matchCookieName(pool, part, index, separatorPart, separatorIndex, name)) {
|
66
|
+
result = extractCookieValue(pool, separatorPart, separatorIndex, endPart, endIndex);
|
67
|
+
done = true;
|
68
|
+
} else {
|
69
|
+
part = endPart;
|
70
|
+
index = endIndex;
|
71
|
+
done = endIndex >= endPart->size;
|
72
|
+
}
|
73
|
+
} else {
|
74
|
+
done = true;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
return result;
|
79
|
+
}
|
80
|
+
|
81
|
+
inline bool
|
82
|
+
findCookieNameValueSeparator(const LString::Part *part, size_t index,
|
83
|
+
const LString::Part **separatorPart, size_t *separatorIndex)
|
84
|
+
{
|
85
|
+
const char *pos;
|
86
|
+
bool result = false;
|
87
|
+
bool done = part == NULL;
|
88
|
+
|
89
|
+
while (!done) {
|
90
|
+
pos = (const char *) memchr(part->data + index, '=', part->size - index);
|
91
|
+
if (pos == NULL) {
|
92
|
+
part = part->next;
|
93
|
+
index = 0;
|
94
|
+
done = part == NULL;
|
95
|
+
} else {
|
96
|
+
*separatorPart = part;
|
97
|
+
*separatorIndex = pos - part->data;
|
98
|
+
result = true;
|
99
|
+
done = true;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
return result;
|
104
|
+
}
|
105
|
+
|
106
|
+
inline bool
|
107
|
+
findCookieEnd(const LString::Part *separatorPart, size_t separatorIndex,
|
108
|
+
const LString::Part **endPart, size_t *endIndex)
|
109
|
+
{
|
110
|
+
const LString::Part *part = separatorPart;
|
111
|
+
size_t index = separatorIndex;
|
112
|
+
const char *pos;
|
113
|
+
bool result = false;
|
114
|
+
bool done = part == NULL;
|
115
|
+
|
116
|
+
while (!done) {
|
117
|
+
pos = (const char *) memchr(part->data + index, ';', part->size - index);
|
118
|
+
if (pos == NULL) {
|
119
|
+
if (part->next == NULL) {
|
120
|
+
// Semicolon not found in entire LString. Return end-of-LString
|
121
|
+
// as cookie end.
|
122
|
+
*endPart = part;
|
123
|
+
*endIndex = part->size;
|
124
|
+
result = true;
|
125
|
+
done = true;
|
126
|
+
} else {
|
127
|
+
part = part->next;
|
128
|
+
index = 0;
|
129
|
+
done = part == NULL;
|
130
|
+
}
|
131
|
+
} else {
|
132
|
+
// Semicolon found.
|
133
|
+
*endPart = part;
|
134
|
+
*endIndex = pos - part->data;
|
135
|
+
result = true;
|
136
|
+
done = true;
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
return result;
|
141
|
+
}
|
142
|
+
|
143
|
+
inline void
|
144
|
+
_matchCookieName_skipWhitespace(LString *str) {
|
145
|
+
LString::Part *part = str->start;
|
146
|
+
size_t pos = 0;
|
147
|
+
bool done = false;
|
148
|
+
|
149
|
+
while (!done) {
|
150
|
+
while (part->data[pos] == ' ' || part->data[pos] == ';') {
|
151
|
+
pos++;
|
152
|
+
}
|
153
|
+
|
154
|
+
if (pos == part->size) {
|
155
|
+
str->start = part->next;
|
156
|
+
str->size -= part->size;
|
157
|
+
part = part->next;
|
158
|
+
if (part == NULL) {
|
159
|
+
assert(str->size == 0);
|
160
|
+
done = true;
|
161
|
+
str->end = NULL;
|
162
|
+
}
|
163
|
+
} else {
|
164
|
+
part->data += pos;
|
165
|
+
part->size -= pos;
|
166
|
+
str->size -= pos;
|
167
|
+
done = true;
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
inline bool
|
173
|
+
matchCookieName(psg_pool_t *pool, const LString::Part *part, size_t index,
|
174
|
+
const LString::Part *separatorPart, size_t separatorIndex,
|
175
|
+
const LString *name)
|
176
|
+
{
|
177
|
+
LString *str = (LString *) psg_palloc(pool, sizeof(LString));
|
178
|
+
psg_lstr_init(str);
|
179
|
+
|
180
|
+
if (part == separatorPart) {
|
181
|
+
assert(index < separatorIndex);
|
182
|
+
psg_lstr_append(str, pool,
|
183
|
+
part->data + index,
|
184
|
+
separatorIndex - index);
|
185
|
+
} else {
|
186
|
+
psg_lstr_append(str, pool,
|
187
|
+
part->data + index,
|
188
|
+
part->size - index);
|
189
|
+
|
190
|
+
part = part->next;
|
191
|
+
while (part != separatorPart) {
|
192
|
+
psg_lstr_append(str, pool, part->data, part->size);
|
193
|
+
part = part->next;
|
194
|
+
}
|
195
|
+
|
196
|
+
if (separatorIndex != 0) {
|
197
|
+
psg_lstr_append(str, pool, separatorPart->data, separatorIndex);
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
_matchCookieName_skipWhitespace(str);
|
202
|
+
|
203
|
+
bool result = psg_lstr_cmp(str, name);
|
204
|
+
psg_lstr_deinit(str);
|
205
|
+
return result;
|
206
|
+
}
|
207
|
+
|
208
|
+
inline LString *
|
209
|
+
extractCookieValue(psg_pool_t *pool,
|
210
|
+
const LString::Part *separatorPart, size_t separatorIndex,
|
211
|
+
const LString::Part *endPart, size_t endIndex)
|
212
|
+
{
|
213
|
+
LString *str = (LString *) psg_palloc(pool, sizeof(LString));
|
214
|
+
psg_lstr_init(str);
|
215
|
+
|
216
|
+
if (separatorPart == endPart) {
|
217
|
+
assert(separatorIndex < endIndex);
|
218
|
+
psg_lstr_append(str, pool,
|
219
|
+
separatorPart->data + separatorIndex + 1,
|
220
|
+
endIndex - separatorIndex - 1);
|
221
|
+
} else {
|
222
|
+
if (separatorIndex < separatorPart->size - 1) {
|
223
|
+
psg_lstr_append(str, pool,
|
224
|
+
separatorPart->data + separatorIndex + 1,
|
225
|
+
separatorPart->size - separatorIndex - 1);
|
226
|
+
}
|
227
|
+
|
228
|
+
const LString::Part *part = separatorPart->next;
|
229
|
+
while (part != endPart) {
|
230
|
+
psg_lstr_append(str, pool, part->data, part->size);
|
231
|
+
part = part->next;
|
232
|
+
}
|
233
|
+
|
234
|
+
if (endIndex != 0) {
|
235
|
+
psg_lstr_append(str, pool, endPart->data, endIndex);
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
return str;
|
240
|
+
}
|
241
|
+
|
242
|
+
|
243
|
+
} // namespace ServerKit
|
244
|
+
} // namespace Passenger
|
245
|
+
|
246
|
+
#endif /* _PASSENGER_SERVER_KIT_COOKIE_UTILS_H_ */
|
@@ -207,6 +207,16 @@ public:
|
|
207
207
|
return watcher.fd;
|
208
208
|
}
|
209
209
|
|
210
|
+
OXT_FORCE_INLINE
|
211
|
+
State getState() const {
|
212
|
+
return Channel::getState();
|
213
|
+
}
|
214
|
+
|
215
|
+
OXT_FORCE_INLINE
|
216
|
+
bool isStarted() const {
|
217
|
+
return Channel::isStarted();
|
218
|
+
}
|
219
|
+
|
210
220
|
OXT_FORCE_INLINE
|
211
221
|
void setDataCallback(DataCallback callback) {
|
212
222
|
Channel::dataCallback = callback;
|
@@ -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
|
*
|
@@ -43,6 +43,8 @@
|
|
43
43
|
#include <ServerKit/Context.h>
|
44
44
|
#include <ServerKit/Errors.h>
|
45
45
|
#include <ServerKit/Channel.h>
|
46
|
+
#include <Utils/json.h>
|
47
|
+
#include <Utils/JsonUtils.h>
|
46
48
|
|
47
49
|
namespace Passenger {
|
48
50
|
namespace ServerKit {
|
@@ -187,8 +189,10 @@ public:
|
|
187
189
|
typedef Channel::DataCallback DataCallback;
|
188
190
|
typedef void (*Callback)(FileBufferedChannel *channel);
|
189
191
|
|
190
|
-
//
|
191
|
-
static const unsigned int MAX_MEMORY_BUFFERING =
|
192
|
+
// 2^32-1 bytes.
|
193
|
+
static const unsigned int MAX_MEMORY_BUFFERING = 4294967295u;
|
194
|
+
// `nbuffers` is 27-bit. This is 2^27-1.
|
195
|
+
static const unsigned int MAX_BUFFERS = 134217727;
|
192
196
|
|
193
197
|
|
194
198
|
private:
|
@@ -197,6 +201,13 @@ private:
|
|
197
201
|
SafeLibevPtr libev;
|
198
202
|
eio_req *req;
|
199
203
|
boost::atomic<bool> canceled;
|
204
|
+
/**
|
205
|
+
* Synchronizes access to `req`. Because all I/O callbacks call
|
206
|
+
* `eioFinished()`, this mutex blocks callbacks until the main
|
207
|
+
* thread is done assigning `req`.
|
208
|
+
* See https://github.com/phusion/passenger/issues/1326
|
209
|
+
*/
|
210
|
+
boost::mutex syncher;
|
200
211
|
|
201
212
|
eio_ssize_t result;
|
202
213
|
int errcode;
|
@@ -213,6 +224,7 @@ private:
|
|
213
224
|
virtual ~IOContext() { }
|
214
225
|
|
215
226
|
void cancel() {
|
227
|
+
boost::lock_guard<boost::mutex> l(syncher);
|
216
228
|
if (req != NULL) {
|
217
229
|
eio_cancel(req);
|
218
230
|
}
|
@@ -225,6 +237,7 @@ private:
|
|
225
237
|
}
|
226
238
|
|
227
239
|
void eioFinished() {
|
240
|
+
boost::lock_guard<boost::mutex> l(syncher);
|
228
241
|
result = req->result;
|
229
242
|
errcode = req->errorno;
|
230
243
|
req = NULL;
|
@@ -337,10 +350,10 @@ private:
|
|
337
350
|
};
|
338
351
|
|
339
352
|
FileBufferedChannelConfig *config;
|
340
|
-
Mode mode;
|
341
|
-
ReaderState readerState;
|
353
|
+
Mode mode: 2;
|
354
|
+
ReaderState readerState: 3;
|
342
355
|
/** Number of buffers in `firstBuffer` + `moreBuffers`. */
|
343
|
-
|
356
|
+
unsigned int nbuffers: 27;
|
344
357
|
|
345
358
|
/**
|
346
359
|
* If an error is encountered, its details are stored here.
|
@@ -390,6 +403,7 @@ private:
|
|
390
403
|
|
391
404
|
void pushBuffer(const MemoryKit::mbuf &buffer) {
|
392
405
|
assert(bytesBuffered + buffer.size() <= MAX_MEMORY_BUFFERING);
|
406
|
+
assert(nbuffers < MAX_BUFFERS);
|
393
407
|
if (nbuffers == 0) {
|
394
408
|
firstBuffer = buffer;
|
395
409
|
} else {
|
@@ -606,7 +620,6 @@ private:
|
|
606
620
|
void channelHasBecomeIdle() {
|
607
621
|
FBC_DEBUG("Reader: underlying channel has become idle");
|
608
622
|
verifyInvariants();
|
609
|
-
//readerState = RS_INACTIVE;
|
610
623
|
readNext();
|
611
624
|
}
|
612
625
|
|
@@ -631,9 +644,9 @@ private:
|
|
631
644
|
};
|
632
645
|
|
633
646
|
void readNextChunkFromFile() {
|
647
|
+
assert(inFileMode->written > 0);
|
634
648
|
size_t size = std::min<size_t>(inFileMode->written,
|
635
|
-
ctx->mbuf_pool
|
636
|
-
ctx->mbuf_pool.mbuf_block_offset);
|
649
|
+
mbuf_pool_data_size(&ctx->mbuf_pool));
|
637
650
|
FBC_DEBUG("Reader: reading next chunk from file");
|
638
651
|
verifyInvariants();
|
639
652
|
ReadContext *readContext = new ReadContext(this);
|
@@ -641,16 +654,34 @@ private:
|
|
641
654
|
readContext->inFileMode = inFileMode;
|
642
655
|
readerState = RS_READING_FROM_FILE;
|
643
656
|
inFileMode->readRequest = readContext;
|
657
|
+
boost::unique_lock<boost::mutex> l(readContext->syncher);
|
644
658
|
readContext->req = eio_read(inFileMode->fd, readContext->buffer.start,
|
645
659
|
size, inFileMode->readOffset, 0, _nextChunkDoneReading, readContext);
|
660
|
+
l.unlock();
|
646
661
|
verifyInvariants();
|
647
662
|
}
|
648
663
|
|
664
|
+
// Since a ReadContext contains an mbuf, we may only destroy it
|
665
|
+
// in the event loop thread.
|
666
|
+
static void destroyReadContext(ReadContext *readContext) {
|
667
|
+
if (readContext->libev->onEventLoopThread()) {
|
668
|
+
destroyReadContext_onEventLoopThread(readContext);
|
669
|
+
} else {
|
670
|
+
readContext->libev->runLater(boost::bind(
|
671
|
+
destroyReadContext_onEventLoopThread,
|
672
|
+
readContext));
|
673
|
+
}
|
674
|
+
}
|
675
|
+
|
676
|
+
static void destroyReadContext_onEventLoopThread(ReadContext *readContext) {
|
677
|
+
delete readContext;
|
678
|
+
}
|
679
|
+
|
649
680
|
static int _nextChunkDoneReading(eio_req *req) {
|
650
681
|
ReadContext *readContext = (ReadContext *) req->data;
|
651
682
|
readContext->eioFinished();
|
652
683
|
if (readContext->isCanceled()) {
|
653
|
-
|
684
|
+
destroyReadContext(readContext);
|
654
685
|
return 0;
|
655
686
|
}
|
656
687
|
|
@@ -666,7 +697,7 @@ private:
|
|
666
697
|
|
667
698
|
static void _nextChunkDoneReading_onEventLoopThread(ReadContext *readContext) {
|
668
699
|
if (readContext->isCanceled()) {
|
669
|
-
|
700
|
+
destroyReadContext(readContext);
|
670
701
|
return;
|
671
702
|
}
|
672
703
|
|
@@ -683,7 +714,7 @@ private:
|
|
683
714
|
int fd = readContext->result;
|
684
715
|
int errcode = readContext->errcode;
|
685
716
|
MemoryKit::mbuf buffer(boost::move(readContext->buffer));
|
686
|
-
|
717
|
+
destroyReadContext(readContext);
|
687
718
|
inFileMode->readRequest = NULL;
|
688
719
|
|
689
720
|
if (fd != -1) {
|
@@ -802,6 +833,7 @@ private:
|
|
802
833
|
inFileMode->writerState = WS_CREATING_FILE;
|
803
834
|
inFileMode->writerRequest = fcContext;
|
804
835
|
|
836
|
+
boost::lock_guard<boost::mutex> l(fcContext->syncher);
|
805
837
|
if (config->delayInFileModeSwitching == 0) {
|
806
838
|
FBC_DEBUG("Writer: creating file " << fcContext->path);
|
807
839
|
fcContext->req = eio_open(fcContext->path.c_str(),
|
@@ -845,6 +877,7 @@ private:
|
|
845
877
|
}
|
846
878
|
|
847
879
|
void bufferFileDoneDelaying(FileCreationContext *fcContext) {
|
880
|
+
boost::lock_guard<boost::mutex> l(fcContext->syncher);
|
848
881
|
FBC_DEBUG("Writer: done delaying in-file mode switching. "
|
849
882
|
"Creating file: " << fcContext->path);
|
850
883
|
fcContext->req = eio_open(fcContext->path.c_str(),
|
@@ -981,19 +1014,37 @@ private:
|
|
981
1014
|
|
982
1015
|
inFileMode->writerState = WS_MOVING;
|
983
1016
|
inFileMode->writerRequest = moveContext;
|
1017
|
+
boost::unique_lock<boost::mutex> l(moveContext->syncher);
|
984
1018
|
moveContext->req = eio_write(inFileMode->fd,
|
985
1019
|
moveContext->buffer.start,
|
986
1020
|
moveContext->buffer.size(),
|
987
1021
|
inFileMode->readOffset + inFileMode->written,
|
988
1022
|
0, _bufferWrittenToFile, moveContext);
|
1023
|
+
l.unlock();
|
989
1024
|
verifyInvariants();
|
990
1025
|
}
|
991
1026
|
|
1027
|
+
// Since a MoveContext contains an mbuf, we may only destroy it
|
1028
|
+
// in the event loop thread.
|
1029
|
+
static void destroyMoveContext(MoveContext *moveContext) {
|
1030
|
+
if (moveContext->libev->onEventLoopThread()) {
|
1031
|
+
destroyMoveContext_onEventLoopThread(moveContext);
|
1032
|
+
} else {
|
1033
|
+
moveContext->libev->runLater(boost::bind(
|
1034
|
+
destroyMoveContext_onEventLoopThread,
|
1035
|
+
moveContext));
|
1036
|
+
}
|
1037
|
+
}
|
1038
|
+
|
1039
|
+
static void destroyMoveContext_onEventLoopThread(MoveContext *moveContext) {
|
1040
|
+
delete moveContext;
|
1041
|
+
}
|
1042
|
+
|
992
1043
|
static int _bufferWrittenToFile(eio_req *req) {
|
993
1044
|
MoveContext *moveContext = static_cast<MoveContext *>(req->data);
|
994
1045
|
moveContext->eioFinished();
|
995
1046
|
if (moveContext->isCanceled()) {
|
996
|
-
|
1047
|
+
destroyMoveContext(moveContext);
|
997
1048
|
return 0;
|
998
1049
|
}
|
999
1050
|
|
@@ -1009,7 +1060,7 @@ private:
|
|
1009
1060
|
|
1010
1061
|
static void _bufferWrittenToFile_onEventLoopThread(MoveContext *moveContext) {
|
1011
1062
|
if (moveContext->isCanceled()) {
|
1012
|
-
|
1063
|
+
destroyMoveContext(moveContext);
|
1013
1064
|
return;
|
1014
1065
|
}
|
1015
1066
|
|
@@ -1040,27 +1091,29 @@ private:
|
|
1040
1091
|
if (generation != this->generation || mode >= ERROR) {
|
1041
1092
|
// buffersFlushedCallback deinitialized this object, or callback
|
1042
1093
|
// called a method that encountered an error.
|
1043
|
-
|
1094
|
+
destroyMoveContext(moveContext);
|
1044
1095
|
return;
|
1045
1096
|
}
|
1046
1097
|
|
1047
1098
|
inFileMode->writerRequest = NULL;
|
1048
|
-
|
1099
|
+
destroyMoveContext(moveContext);
|
1049
1100
|
moveNextBufferToFile();
|
1050
1101
|
} else {
|
1051
1102
|
FBC_DEBUG("Writer: move incomplete, proceeding " <<
|
1052
1103
|
"with writing rest of buffer");
|
1104
|
+
boost::unique_lock<boost::mutex> l(moveContext->syncher);
|
1053
1105
|
moveContext->req = eio_write(inFileMode->fd,
|
1054
1106
|
moveContext->buffer.start + moveContext->written,
|
1055
1107
|
moveContext->buffer.size() - moveContext->written,
|
1056
1108
|
inFileMode->readOffset + inFileMode->written,
|
1057
1109
|
0, _bufferWrittenToFile, moveContext);
|
1110
|
+
l.unlock();
|
1058
1111
|
verifyInvariants();
|
1059
1112
|
}
|
1060
1113
|
} else {
|
1061
1114
|
FBC_DEBUG("Writer: file write failed");
|
1062
1115
|
int errcode = moveContext->errcode;
|
1063
|
-
|
1116
|
+
destroyMoveContext(moveContext);
|
1064
1117
|
inFileMode->writerRequest = NULL;
|
1065
1118
|
inFileMode->writerState = WS_TERMINATED;
|
1066
1119
|
setError(errcode, __FILE__, __LINE__);
|
@@ -1141,6 +1194,42 @@ private:
|
|
1141
1194
|
inFileMode->writerState = WS_INACTIVE;
|
1142
1195
|
}
|
1143
1196
|
|
1197
|
+
const char *getReaderStateString() const {
|
1198
|
+
switch (readerState) {
|
1199
|
+
case RS_INACTIVE:
|
1200
|
+
return "RS_INACTIVE";
|
1201
|
+
case RS_FEEDING:
|
1202
|
+
return "RS_FEEDING";
|
1203
|
+
case RS_FEEDING_EOF:
|
1204
|
+
return "RS_FEEDING_EOF";
|
1205
|
+
case RS_WAITING_FOR_CHANNEL_IDLE:
|
1206
|
+
return "RS_WAITING_FOR_CHANNEL_IDLE";
|
1207
|
+
case RS_READING_FROM_FILE:
|
1208
|
+
return "RS_READING_FROM_FILE";
|
1209
|
+
case RS_TERMINATED:
|
1210
|
+
return "RS_TERMINATED";
|
1211
|
+
default:
|
1212
|
+
P_BUG("Unknown readerState");
|
1213
|
+
return NULL;
|
1214
|
+
}
|
1215
|
+
}
|
1216
|
+
|
1217
|
+
const char *getWriterStateString() const {
|
1218
|
+
switch (inFileMode->writerState) {
|
1219
|
+
case WS_INACTIVE:
|
1220
|
+
return "WS_INACTIVE";
|
1221
|
+
case WS_CREATING_FILE:
|
1222
|
+
return "WS_CREATING_FILE";
|
1223
|
+
case WS_MOVING:
|
1224
|
+
return "WS_MOVING";
|
1225
|
+
case WS_TERMINATED:
|
1226
|
+
return "WS_TERMINATED";
|
1227
|
+
default:
|
1228
|
+
P_BUG("Unknown writerState");
|
1229
|
+
return NULL;
|
1230
|
+
}
|
1231
|
+
}
|
1232
|
+
|
1144
1233
|
void verifyInvariants() const {
|
1145
1234
|
#ifndef NDEBUG
|
1146
1235
|
if (mode >= ERROR) {
|
@@ -1353,10 +1442,32 @@ public:
|
|
1353
1442
|
return inFileMode->writerState;
|
1354
1443
|
}
|
1355
1444
|
|
1445
|
+
/**
|
1446
|
+
* Returns the number of bytes buffered in memory.
|
1447
|
+
*/
|
1356
1448
|
unsigned int getBytesBuffered() const {
|
1357
1449
|
return bytesBuffered;
|
1358
1450
|
}
|
1359
1451
|
|
1452
|
+
/**
|
1453
|
+
* Returns the number of bytes that are buffered on disk
|
1454
|
+
* and have not yet been read.
|
1455
|
+
*/
|
1456
|
+
boost::uint64_t getBytesBufferedOnDisk() const {
|
1457
|
+
if (mode == IN_FILE_MODE && inFileMode->written >= 0) {
|
1458
|
+
return inFileMode->written;
|
1459
|
+
} else {
|
1460
|
+
return 0;
|
1461
|
+
}
|
1462
|
+
}
|
1463
|
+
|
1464
|
+
/**
|
1465
|
+
* Returns the total bytes buffered, both in-memory and on disk.
|
1466
|
+
*/
|
1467
|
+
boost::uint64_t getTotalBytesBuffered() const {
|
1468
|
+
return bytesBuffered + getBytesBufferedOnDisk();
|
1469
|
+
}
|
1470
|
+
|
1360
1471
|
bool ended() const {
|
1361
1472
|
return (hasBuffers() && peekLastBuffer().empty())
|
1362
1473
|
|| mode >= ERROR || Channel::ended();
|
@@ -1370,14 +1481,27 @@ public:
|
|
1370
1481
|
return bytesBuffered >= config->threshold;
|
1371
1482
|
}
|
1372
1483
|
|
1484
|
+
OXT_FORCE_INLINE
|
1373
1485
|
void setDataCallback(DataCallback callback) {
|
1374
1486
|
Channel::dataCallback = callback;
|
1375
1487
|
}
|
1376
1488
|
|
1489
|
+
OXT_FORCE_INLINE
|
1490
|
+
Callback getBuffersFlushedCallback() const {
|
1491
|
+
return buffersFlushedCallback;
|
1492
|
+
}
|
1493
|
+
|
1494
|
+
OXT_FORCE_INLINE
|
1377
1495
|
void setBuffersFlushedCallback(Callback callback) {
|
1378
1496
|
buffersFlushedCallback = callback;
|
1379
1497
|
}
|
1380
1498
|
|
1499
|
+
OXT_FORCE_INLINE
|
1500
|
+
Callback getDataFlushedCallback() const {
|
1501
|
+
return dataFlushedCallback;
|
1502
|
+
}
|
1503
|
+
|
1504
|
+
OXT_FORCE_INLINE
|
1381
1505
|
void setDataFlushedCallback(Callback callback) {
|
1382
1506
|
dataFlushedCallback = callback;
|
1383
1507
|
}
|
@@ -1387,9 +1511,41 @@ public:
|
|
1387
1511
|
return Channel::hooks;
|
1388
1512
|
}
|
1389
1513
|
|
1514
|
+
OXT_FORCE_INLINE
|
1390
1515
|
void setHooks(Hooks *hooks) {
|
1391
1516
|
Channel::hooks = hooks;
|
1392
1517
|
}
|
1518
|
+
|
1519
|
+
Json::Value inspectAsJson() const {
|
1520
|
+
Json::Value doc;
|
1521
|
+
|
1522
|
+
switch (mode) {
|
1523
|
+
case IN_MEMORY_MODE:
|
1524
|
+
doc["mode"] = "IN_MEMORY_MODE";
|
1525
|
+
break;
|
1526
|
+
case IN_FILE_MODE:
|
1527
|
+
doc["mode"] = "IN_FILE_MODE";
|
1528
|
+
doc["writer_state"] = getWriterStateString();
|
1529
|
+
doc["read_offset"] = byteSizeToJson(inFileMode->readOffset);
|
1530
|
+
doc["written"] = signedByteSizeToJson(inFileMode->written);
|
1531
|
+
break;
|
1532
|
+
case ERROR:
|
1533
|
+
doc["mode"] = "ERROR";
|
1534
|
+
break;
|
1535
|
+
case ERROR_WAITING:
|
1536
|
+
doc["mode"] = "ERROR_WAITING";
|
1537
|
+
break;
|
1538
|
+
default:
|
1539
|
+
break;
|
1540
|
+
}
|
1541
|
+
|
1542
|
+
doc["reader_state"] = getReaderStateString();
|
1543
|
+
doc["nbuffers"] = nbuffers;
|
1544
|
+
doc["bytes_buffered"] = byteSizeToJson(getBytesBuffered());
|
1545
|
+
doc["callback_in_progress"] = !acceptingInput();
|
1546
|
+
|
1547
|
+
return doc;
|
1548
|
+
}
|
1393
1549
|
};
|
1394
1550
|
|
1395
1551
|
|