passenger 5.0.9 → 5.0.10
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 +15 -0
- data/CONTRIBUTORS +6 -0
- data/README.md +1 -1
- data/bin/passenger-install-apache2-module +24 -11
- data/bin/passenger-status +29 -14
- data/build/agents.rb +12 -10
- data/build/cxx_tests.rb +30 -30
- data/doc/Design and Architecture.html +1 -10
- data/doc/Design and Architecture.txt +1 -6
- data/doc/Users guide Apache.html +1 -19
- data/doc/Users guide Apache.txt +1 -1
- data/doc/Users guide Nginx.html +2 -20
- data/doc/Users guide Nginx.txt +2 -2
- data/doc/users_guide_snippets/tips.txt +0 -9
- data/ext/common/ApplicationPool2/ApiKey.h +158 -0
- data/ext/common/ApplicationPool2/BasicGroupInfo.h +81 -0
- data/ext/common/ApplicationPool2/BasicProcessInfo.h +106 -0
- data/ext/common/ApplicationPool2/Common.h +5 -44
- data/ext/common/ApplicationPool2/Context.h +94 -0
- data/ext/common/ApplicationPool2/Group.h +130 -1205
- data/ext/common/ApplicationPool2/Group/InitializationAndShutdown.cpp +190 -0
- data/ext/common/ApplicationPool2/Group/InternalUtils.cpp +329 -0
- data/ext/common/ApplicationPool2/Group/LifetimeAndBasics.cpp +103 -0
- data/ext/common/ApplicationPool2/{Pool/Debug.h → Group/Miscellaneous.cpp} +40 -38
- data/ext/common/ApplicationPool2/Group/OutOfBandWork.cpp +323 -0
- data/ext/common/ApplicationPool2/Group/ProcessListManagement.cpp +606 -0
- data/ext/common/ApplicationPool2/Group/SessionManagement.cpp +337 -0
- data/ext/common/ApplicationPool2/Group/SpawningAndRestarting.cpp +478 -0
- data/ext/common/ApplicationPool2/Group/StateInspection.cpp +197 -0
- data/ext/common/ApplicationPool2/Group/Verification.cpp +159 -0
- data/ext/common/ApplicationPool2/Implementation.cpp +19 -1401
- data/ext/common/ApplicationPool2/Options.h +5 -5
- data/ext/common/ApplicationPool2/Pool.h +260 -815
- data/ext/common/ApplicationPool2/Pool/{AnalyticsCollection.h → AnalyticsCollection.cpp} +55 -56
- data/ext/common/ApplicationPool2/Pool/{GarbageCollection.h → GarbageCollection.cpp} +49 -49
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.cpp +241 -0
- data/ext/common/ApplicationPool2/Pool/GroupUtils.cpp +276 -0
- data/ext/common/ApplicationPool2/Pool/InitializationAndShutdown.cpp +145 -0
- data/ext/common/ApplicationPool2/Pool/Miscellaneous.cpp +244 -0
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.cpp +330 -0
- data/ext/common/ApplicationPool2/Pool/StateInspection.cpp +299 -0
- data/ext/common/ApplicationPool2/Process.h +399 -205
- data/ext/common/ApplicationPool2/Session.h +70 -28
- data/ext/common/ApplicationPool2/Socket.h +1 -0
- data/ext/common/Constants.h +11 -3
- data/ext/common/Exceptions.h +1 -1
- data/ext/common/Logging.cpp +9 -4
- data/ext/common/Logging.h +6 -0
- data/ext/common/ServerKit/HttpServer.h +225 -215
- data/ext/common/ServerKit/Server.h +57 -57
- data/ext/common/SpawningKit/BackgroundIOCapturer.h +160 -0
- data/ext/common/SpawningKit/Config.h +107 -0
- data/ext/common/{ApplicationPool2 → SpawningKit}/DirectSpawner.h +17 -16
- data/ext/common/{ApplicationPool2 → SpawningKit}/DummySpawner.h +33 -33
- data/ext/common/{ApplicationPool2/SpawnerFactory.h → SpawningKit/Factory.h} +17 -17
- data/ext/common/{ApplicationPool2/ComponentInfo.h → SpawningKit/Options.h} +8 -21
- data/ext/common/SpawningKit/PipeWatcher.h +148 -0
- data/ext/common/{ApplicationPool2/PipeWatcher.h → SpawningKit/Result.h} +15 -33
- data/ext/common/{ApplicationPool2 → SpawningKit}/SmartSpawner.h +52 -57
- data/ext/common/{ApplicationPool2 → SpawningKit}/Spawner.h +83 -371
- data/ext/common/SpawningKit/UserSwitchingRules.h +265 -0
- data/ext/common/Utils/BufferedIO.h +24 -0
- data/ext/common/{ApplicationPool2/SpawnObject.h → Utils/ClassUtils.h} +24 -51
- data/ext/common/Utils/IOUtils.cpp +70 -0
- data/ext/common/Utils/IOUtils.h +19 -0
- data/ext/common/Utils/JsonUtils.h +113 -0
- data/ext/common/Utils/StrIntUtils.h +29 -0
- data/ext/common/Utils/json.h +1 -1
- data/ext/common/agents/ApiServerUtils.h +941 -0
- data/ext/common/agents/HelperAgent/{AdminServer.h → ApiServer.h} +163 -365
- data/ext/common/agents/HelperAgent/Main.cpp +86 -88
- data/ext/common/agents/HelperAgent/OptionParser.h +9 -10
- data/ext/common/agents/HelperAgent/RequestHandler/BufferBody.cpp +3 -0
- data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +2 -0
- data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +1 -1
- data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +2 -2
- data/ext/common/agents/LoggingAgent/ApiServer.h +279 -0
- data/ext/common/agents/LoggingAgent/Main.cpp +41 -51
- data/ext/common/agents/LoggingAgent/OptionParser.h +11 -11
- data/ext/common/agents/Watchdog/ApiServer.h +311 -0
- data/ext/common/agents/Watchdog/Main.cpp +91 -65
- data/helper-scripts/prespawn +2 -0
- data/lib/phusion_passenger.rb +1 -1
- data/lib/phusion_passenger/admin_tools/instance.rb +1 -1
- data/lib/phusion_passenger/common_library.rb +27 -14
- data/lib/phusion_passenger/config/{admin_command_command.rb → api_call_command.rb} +19 -16
- data/lib/phusion_passenger/config/detach_process_command.rb +6 -3
- data/lib/phusion_passenger/config/main.rb +3 -5
- data/lib/phusion_passenger/config/reopen_logs_command.rb +29 -7
- data/lib/phusion_passenger/config/restart_app_command.rb +13 -4
- data/lib/phusion_passenger/config/utils.rb +15 -8
- data/lib/phusion_passenger/constants.rb +6 -2
- data/lib/phusion_passenger/platform_info/apache.rb +4 -0
- data/lib/phusion_passenger/platform_info/apache_detector.rb +18 -3
- data/resources/templates/apache2/mpm_unknown.txt.erb +20 -0
- metadata +42 -21
- metadata.gz.asc +7 -7
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.h +0 -127
- data/ext/common/ApplicationPool2/Pool/Inspection.h +0 -219
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.h +0 -85
- data/ext/common/ApplicationPool2/SuperGroup.h +0 -706
- data/ext/common/agents/LoggingAgent/AdminServer.h +0 -435
- data/ext/common/agents/Watchdog/AdminServer.h +0 -432
@@ -22,16 +22,18 @@
|
|
22
22
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
23
|
* THE SOFTWARE.
|
24
24
|
*/
|
25
|
-
#ifndef
|
26
|
-
#define
|
25
|
+
#ifndef _PASSENGER_SERVER_AGENT_API_SERVER_H_
|
26
|
+
#define _PASSENGER_SERVER_AGENT_API_SERVER_H_
|
27
27
|
|
28
28
|
#include <boost/regex.hpp>
|
29
29
|
#include <oxt/thread.hpp>
|
30
30
|
#include <sstream>
|
31
31
|
#include <string>
|
32
32
|
#include <cstring>
|
33
|
+
#include <sys/types.h>
|
33
34
|
|
34
35
|
#include <agents/HelperAgent/RequestHandler.h>
|
36
|
+
#include <agents/ApiServerUtils.h>
|
35
37
|
#include <ApplicationPool2/Pool.h>
|
36
38
|
#include <ServerKit/HttpServer.h>
|
37
39
|
#include <DataStructures/LString.h>
|
@@ -42,6 +44,8 @@
|
|
42
44
|
#include <Utils/StrIntUtils.h>
|
43
45
|
#include <Utils/modp_b64.h>
|
44
46
|
#include <Utils/json.h>
|
47
|
+
#include <Utils/BufferedIO.h>
|
48
|
+
#include <Utils/MessageIO.h>
|
45
49
|
|
46
50
|
namespace Passenger {
|
47
51
|
namespace ServerAgent {
|
@@ -53,26 +57,14 @@ class Request: public ServerKit::BaseHttpRequest {
|
|
53
57
|
public:
|
54
58
|
string body;
|
55
59
|
Json::Value jsonBody;
|
60
|
+
Authorization authorization;
|
56
61
|
|
57
62
|
DEFINE_SERVER_KIT_BASE_HTTP_REQUEST_FOOTER(Request);
|
58
63
|
};
|
59
64
|
|
60
|
-
class
|
61
|
-
public:
|
62
|
-
enum PrivilegeLevel {
|
63
|
-
NONE,
|
64
|
-
READONLY,
|
65
|
-
FULL
|
66
|
-
};
|
67
|
-
|
68
|
-
struct Authorization {
|
69
|
-
PrivilegeLevel level;
|
70
|
-
string username;
|
71
|
-
string password;
|
72
|
-
};
|
73
|
-
|
65
|
+
class ApiServer: public ServerKit::HttpServer<ApiServer, ServerKit::HttpClient<Request> > {
|
74
66
|
private:
|
75
|
-
typedef ServerKit::HttpServer<
|
67
|
+
typedef ServerKit::HttpServer<ApiServer, ServerKit::HttpClient<Request> > ParentClass;
|
76
68
|
typedef ServerKit::HttpClient<Request> Client;
|
77
69
|
typedef ServerKit::HeaderTable HeaderTable;
|
78
70
|
|
@@ -82,57 +74,6 @@ private:
|
|
82
74
|
return boost::regex_match(str.data(), str.data() + str.size(), e);
|
83
75
|
}
|
84
76
|
|
85
|
-
bool parseAuthorizationHeader(Request *req, string &username,
|
86
|
-
string &password) const
|
87
|
-
{
|
88
|
-
const LString *auth = req->headers.lookup("authorization");
|
89
|
-
|
90
|
-
if (auth == NULL || auth->size <= 6 || !psg_lstr_cmp(auth, "Basic ", 6)) {
|
91
|
-
return false;
|
92
|
-
}
|
93
|
-
|
94
|
-
auth = psg_lstr_make_contiguous(auth, req->pool);
|
95
|
-
string authData = modp::b64_decode(
|
96
|
-
auth->start->data + sizeof("Basic ") - 1,
|
97
|
-
auth->size - (sizeof("Basic ") - 1));
|
98
|
-
string::size_type pos = authData.find(':');
|
99
|
-
if (pos == string::npos) {
|
100
|
-
return false;
|
101
|
-
}
|
102
|
-
|
103
|
-
username = authData.substr(0, pos);
|
104
|
-
password = authData.substr(pos + 1);
|
105
|
-
return true;
|
106
|
-
}
|
107
|
-
|
108
|
-
const Authorization *lookupAuthorizationRecord(const StaticString &username) const {
|
109
|
-
vector<Authorization>::const_iterator it, end = authorizations.end();
|
110
|
-
|
111
|
-
for (it = authorizations.begin(); it != end; it++) {
|
112
|
-
if (it->username == username) {
|
113
|
-
return &(*it);
|
114
|
-
}
|
115
|
-
}
|
116
|
-
|
117
|
-
return NULL;
|
118
|
-
}
|
119
|
-
|
120
|
-
bool authorize(Client *client, Request *req, PrivilegeLevel level) const {
|
121
|
-
if (authorizations.empty()) {
|
122
|
-
return true;
|
123
|
-
}
|
124
|
-
|
125
|
-
string username, password;
|
126
|
-
if (!parseAuthorizationHeader(req, username, password)) {
|
127
|
-
return false;
|
128
|
-
}
|
129
|
-
|
130
|
-
const Authorization *auth = lookupAuthorizationRecord(username);
|
131
|
-
return auth != NULL
|
132
|
-
&& auth->level >= level
|
133
|
-
&& constantTimeCompare(password, auth->password);
|
134
|
-
}
|
135
|
-
|
136
77
|
int extractThreadNumberFromClientName(const string &clientName) const {
|
137
78
|
boost::smatch results;
|
138
79
|
boost::regex re("^([0-9]+)-.*");
|
@@ -146,42 +87,48 @@ private:
|
|
146
87
|
return stringToUint(results.str(1));
|
147
88
|
}
|
148
89
|
|
149
|
-
static VariantMap parseQueryString(const StaticString &query) {
|
150
|
-
VariantMap params;
|
151
|
-
const char *pos = query.data();
|
152
|
-
const char *end = query.data() + query.size();
|
153
|
-
|
154
|
-
while (pos < end) {
|
155
|
-
const char *assignmentPos = (const char *) memchr(pos, '=', end - pos);
|
156
|
-
if (assignmentPos != NULL) {
|
157
|
-
string name = urldecode(StaticString(pos, assignmentPos - pos));
|
158
|
-
const char *sepPos = (const char *) memchr(assignmentPos + 1, '&',
|
159
|
-
end - assignmentPos - 1);
|
160
|
-
if (sepPos != NULL) {
|
161
|
-
string value = urldecode(StaticString(assignmentPos + 1,
|
162
|
-
sepPos - assignmentPos - 1));
|
163
|
-
params.set(name, value);
|
164
|
-
pos = sepPos + 1;
|
165
|
-
} else {
|
166
|
-
StaticString value(assignmentPos + 1, end - assignmentPos - 1);
|
167
|
-
params.set(name, value);
|
168
|
-
pos = end;
|
169
|
-
}
|
170
|
-
} else {
|
171
|
-
throw SyntaxError("Invalid query string format");
|
172
|
-
}
|
173
|
-
}
|
174
|
-
|
175
|
-
return params;
|
176
|
-
}
|
177
|
-
|
178
90
|
static void disconnectClient(RequestHandler *rh, string clientName) {
|
179
91
|
rh->disconnect(clientName);
|
180
92
|
}
|
181
93
|
|
94
|
+
void route(Client *client, Request *req, const StaticString &path) {
|
95
|
+
if (path == P_STATIC_STRING("/server.json")) {
|
96
|
+
processServerStatus(client, req);
|
97
|
+
} else if (regex_match(path, serverConnectionPath)) {
|
98
|
+
processServerConnectionOperation(client, req);
|
99
|
+
} else if (path == P_STATIC_STRING("/pool.xml")) {
|
100
|
+
processPoolStatusXml(client, req);
|
101
|
+
} else if (path == P_STATIC_STRING("/pool.txt")) {
|
102
|
+
processPoolStatusTxt(client, req);
|
103
|
+
} else if (path == P_STATIC_STRING("/pool/restart_app_group.json")) {
|
104
|
+
processPoolRestartAppGroup(client, req);
|
105
|
+
} else if (path == P_STATIC_STRING("/pool/detach_process.json")) {
|
106
|
+
processPoolDetachProcess(client, req);
|
107
|
+
} else if (path == P_STATIC_STRING("/backtraces.txt")) {
|
108
|
+
apiServerProcessBacktraces(this, client, req);
|
109
|
+
} else if (path == P_STATIC_STRING("/ping.json")) {
|
110
|
+
apiServerProcessPing(this, client, req);
|
111
|
+
} else if (path == P_STATIC_STRING("/version.json")) {
|
112
|
+
apiServerProcessVersion(this, client, req);
|
113
|
+
} else if (path == P_STATIC_STRING("/shutdown.json")) {
|
114
|
+
apiServerProcessShutdown(this, client, req);
|
115
|
+
} else if (path == P_STATIC_STRING("/gc.json")) {
|
116
|
+
processGc(client, req);
|
117
|
+
} else if (path == P_STATIC_STRING("/config.json")) {
|
118
|
+
processConfig(client, req);
|
119
|
+
} else if (path == P_STATIC_STRING("/reinherit_logs.json")) {
|
120
|
+
apiServerProcessReinheritLogs(this, client, req,
|
121
|
+
instanceDir, fdPassingPassword);
|
122
|
+
} else if (path == P_STATIC_STRING("/reopen_logs.json")) {
|
123
|
+
apiServerProcessReopenLogs(this, client, req);
|
124
|
+
} else {
|
125
|
+
apiServerRespondWith404(this, client, req);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
182
129
|
void processServerConnectionOperation(Client *client, Request *req) {
|
183
|
-
if (!
|
184
|
-
|
130
|
+
if (!authorizeAdminOperation(this, client, req)) {
|
131
|
+
apiServerRespondWith401(this, client, req);
|
185
132
|
} else if (req->method == HTTP_DELETE) {
|
186
133
|
StaticString path = req->getPathWithoutQueryString();
|
187
134
|
boost::smatch results;
|
@@ -193,8 +140,6 @@ private:
|
|
193
140
|
}
|
194
141
|
|
195
142
|
int threadNumber = extractThreadNumberFromClientName(results.str(1));
|
196
|
-
P_WARN(results.str(1));
|
197
|
-
P_WARN(threadNumber);
|
198
143
|
if (threadNumber < 1 || (unsigned int) threadNumber > requestHandlers.size()) {
|
199
144
|
HeaderTable headers;
|
200
145
|
headers.insert(req->pool, "Content-Type", "application/json");
|
@@ -217,7 +162,7 @@ private:
|
|
217
162
|
endRequest(&client, &req);
|
218
163
|
}
|
219
164
|
} else {
|
220
|
-
|
165
|
+
apiServerRespondWith405(this, client, req);
|
221
166
|
}
|
222
167
|
}
|
223
168
|
|
@@ -226,7 +171,7 @@ private:
|
|
226
171
|
}
|
227
172
|
|
228
173
|
void processServerStatus(Client *client, Request *req) {
|
229
|
-
if (
|
174
|
+
if (authorizeStateInspectionOperation(this, client, req)) {
|
230
175
|
HeaderTable headers;
|
231
176
|
headers.insert(req->pool, "Content-Type", "application/json");
|
232
177
|
|
@@ -247,93 +192,105 @@ private:
|
|
247
192
|
endRequest(&client, &req);
|
248
193
|
}
|
249
194
|
} else {
|
250
|
-
|
195
|
+
apiServerRespondWith401(this, client, req);
|
251
196
|
}
|
252
197
|
}
|
253
198
|
|
254
199
|
void processPoolStatusXml(Client *client, Request *req) {
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
200
|
+
Authorization auth(authorize(this, client, req));
|
201
|
+
if (auth.canReadPool) {
|
202
|
+
ApplicationPool2::Pool::ToXmlOptions options(
|
203
|
+
parseQueryString(req->getQueryString()));
|
204
|
+
options.uid = auth.uid;
|
205
|
+
options.apiKey = auth.apiKey;
|
206
|
+
|
207
|
+
HeaderTable headers;
|
208
|
+
headers.insert(req->pool, "Content-Type", "text/xml");
|
209
|
+
writeSimpleResponse(client, 200, &headers,
|
210
|
+
psg_pstrdup(req->pool, appPool->toXml(options)));
|
266
211
|
if (!req->ended()) {
|
267
212
|
endRequest(&client, &req);
|
268
213
|
}
|
269
214
|
} else {
|
270
|
-
|
215
|
+
apiServerRespondWith401(this, client, req);
|
271
216
|
}
|
272
217
|
}
|
273
218
|
|
274
219
|
void processPoolStatusTxt(Client *client, Request *req) {
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
220
|
+
Authorization auth(authorize(this, client, req));
|
221
|
+
if (auth.canReadPool) {
|
222
|
+
ApplicationPool2::Pool::InspectOptions options(
|
223
|
+
parseQueryString(req->getQueryString()));
|
224
|
+
options.uid = auth.uid;
|
225
|
+
options.apiKey = auth.apiKey;
|
226
|
+
|
227
|
+
HeaderTable headers;
|
228
|
+
headers.insert(req->pool, "Content-Type", "text/plain");
|
229
|
+
writeSimpleResponse(client, 200, &headers,
|
230
|
+
psg_pstrdup(req->pool, appPool->inspect(options)));
|
286
231
|
if (!req->ended()) {
|
287
232
|
endRequest(&client, &req);
|
288
233
|
}
|
289
234
|
} else {
|
290
|
-
|
235
|
+
apiServerRespondWith401(this, client, req);
|
291
236
|
}
|
292
237
|
}
|
293
238
|
|
294
239
|
void processPoolRestartAppGroup(Client *client, Request *req) {
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
240
|
+
Authorization auth(authorize(this, client, req));
|
241
|
+
if (!auth.canModifyPool) {
|
242
|
+
apiServerRespondWith401(this, client, req);
|
243
|
+
} else if (req->method != HTTP_POST) {
|
244
|
+
apiServerRespondWith405(this, client, req);
|
299
245
|
} else if (!req->hasBody()) {
|
300
246
|
endAsBadRequest(&client, &req, "Body required");
|
301
247
|
} else if (requestBodyExceedsLimit(client, req)) {
|
302
|
-
|
248
|
+
apiServerRespondWith413(this, client, req);
|
249
|
+
} else {
|
250
|
+
req->authorization = auth;
|
251
|
+
// Continues in processPoolRestartAppGroupBody().
|
303
252
|
}
|
304
|
-
// Continues in processPoolRestartAppGroupBody().
|
305
253
|
}
|
306
254
|
|
307
255
|
void processPoolRestartAppGroupBody(Client *client, Request *req) {
|
308
|
-
HeaderTable headers;
|
309
|
-
RestartMethod method = ApplicationPool2::RM_DEFAULT;
|
310
|
-
|
311
|
-
headers.insert(req->pool, "Content-Type", "application/json");
|
312
|
-
headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
|
313
|
-
|
314
256
|
if (!req->jsonBody.isMember("name")) {
|
315
257
|
endAsBadRequest(&client, &req, "Name required");
|
316
258
|
return;
|
317
259
|
}
|
318
260
|
|
261
|
+
ApplicationPool2::Pool::RestartOptions options;
|
262
|
+
options.uid = req->authorization.uid;
|
263
|
+
options.apiKey = req->authorization.apiKey;
|
319
264
|
if (req->jsonBody.isMember("restart_method")) {
|
320
265
|
string restartMethodString = req->jsonBody["restart_method"].asString();
|
321
266
|
if (restartMethodString == "blocking") {
|
322
|
-
method = RM_BLOCKING;
|
267
|
+
options.method = RM_BLOCKING;
|
323
268
|
} else if (restartMethodString == "rolling") {
|
324
|
-
method = RM_ROLLING;
|
269
|
+
options.method = RM_ROLLING;
|
325
270
|
} else {
|
326
271
|
endAsBadRequest(&client, &req, "Unsupported restart method");
|
327
272
|
return;
|
328
273
|
}
|
329
274
|
}
|
330
275
|
|
276
|
+
bool result;
|
331
277
|
const char *response;
|
332
|
-
|
278
|
+
try {
|
279
|
+
result = appPool->restartGroupByName(req->jsonBody["name"].asString(),
|
280
|
+
options);
|
281
|
+
} catch (const SecurityException &) {
|
282
|
+
apiServerRespondWith401(this, client, req);
|
283
|
+
return;
|
284
|
+
}
|
285
|
+
if (result) {
|
333
286
|
response = "{ \"restarted\": true }";
|
334
287
|
} else {
|
335
288
|
response = "{ \"restarted\": false }";
|
336
289
|
}
|
290
|
+
|
291
|
+
HeaderTable headers;
|
292
|
+
headers.insert(req->pool, "Content-Type", "application/json");
|
293
|
+
headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
|
337
294
|
writeSimpleResponse(client, 200, &headers, response);
|
338
295
|
|
339
296
|
if (!req->ended()) {
|
@@ -342,82 +299,52 @@ private:
|
|
342
299
|
}
|
343
300
|
|
344
301
|
void processPoolDetachProcess(Client *client, Request *req) {
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
302
|
+
Authorization auth(authorize(this, client, req));
|
303
|
+
if (!auth.canModifyPool) {
|
304
|
+
apiServerRespondWith401(this, client, req);
|
305
|
+
} else if (req->method != HTTP_POST) {
|
306
|
+
apiServerRespondWith405(this, client, req);
|
349
307
|
} else if (!req->hasBody()) {
|
350
308
|
endAsBadRequest(&client, &req, "Body required");
|
351
309
|
} else if (requestBodyExceedsLimit(client, req)) {
|
352
|
-
|
310
|
+
apiServerRespondWith413(this, client, req);
|
311
|
+
} else {
|
312
|
+
req->authorization = auth;
|
313
|
+
// Continues in processPoolDetachProcessBody().
|
353
314
|
}
|
354
|
-
// Continues in processPoolDetachProcessBody().
|
355
315
|
}
|
356
316
|
|
357
317
|
void processPoolDetachProcessBody(Client *client, Request *req) {
|
358
|
-
HeaderTable headers;
|
359
|
-
const char *response;
|
360
|
-
|
361
|
-
headers.insert(req->pool, "Content-Type", "application/json");
|
362
|
-
headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
|
363
|
-
|
364
318
|
if (req->jsonBody.isMember("pid")) {
|
365
319
|
pid_t pid = (pid_t) req->jsonBody["pid"].asUInt();
|
366
|
-
|
320
|
+
ApplicationPool2::Pool::AuthenticationOptions options;
|
321
|
+
options.uid = req->authorization.uid;
|
322
|
+
options.apiKey = req->authorization.apiKey;
|
323
|
+
|
324
|
+
bool result;
|
325
|
+
try {
|
326
|
+
result = appPool->detachProcess(pid, options);
|
327
|
+
} catch (const SecurityException &) {
|
328
|
+
apiServerRespondWith401(this, client, req);
|
329
|
+
return;
|
330
|
+
}
|
331
|
+
|
332
|
+
const char *response;
|
333
|
+
if (result) {
|
367
334
|
response = "{ \"detached\": true }";
|
368
335
|
} else {
|
369
336
|
response = "{ \"detached\": false }";
|
370
337
|
}
|
371
|
-
writeSimpleResponse(client, 200, &headers, response);
|
372
|
-
if (!req->ended()) {
|
373
|
-
endRequest(&client, &req);
|
374
|
-
}
|
375
|
-
} else {
|
376
|
-
endAsBadRequest(&client, &req, "PID required");
|
377
|
-
}
|
378
|
-
}
|
379
338
|
|
380
|
-
void processBacktraces(Client *client, Request *req) {
|
381
|
-
if (authorize(client, req, READONLY)) {
|
382
339
|
HeaderTable headers;
|
383
|
-
headers.insert(req->pool, "Content-Type", "text/plain");
|
384
|
-
writeSimpleResponse(client, 200, &headers,
|
385
|
-
psg_pstrdup(req->pool, oxt::thread::all_backtraces()));
|
386
|
-
if (!req->ended()) {
|
387
|
-
endRequest(&client, &req);
|
388
|
-
}
|
389
|
-
} else {
|
390
|
-
respondWith401(client, req);
|
391
|
-
}
|
392
|
-
}
|
393
|
-
|
394
|
-
void processPing(Client *client, Request *req) {
|
395
|
-
if (authorize(client, req, READONLY)) {
|
396
|
-
HeaderTable headers;
|
397
|
-
headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
|
398
340
|
headers.insert(req->pool, "Content-Type", "application/json");
|
399
|
-
|
400
|
-
|
401
|
-
endRequest(&client, &req);
|
402
|
-
}
|
403
|
-
} else {
|
404
|
-
respondWith401(client, req);
|
405
|
-
}
|
406
|
-
}
|
407
|
-
|
408
|
-
void processShutdown(Client *client, Request *req) {
|
409
|
-
if (req->method != HTTP_PUT) {
|
410
|
-
respondWith405(client, req);
|
411
|
-
} else if (authorize(client, req, FULL)) {
|
412
|
-
HeaderTable headers;
|
413
|
-
headers.insert(req->pool, "Content-Type", "application/json");
|
414
|
-
exitEvent->notify();
|
415
|
-
writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }");
|
341
|
+
headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
|
342
|
+
writeSimpleResponse(client, 200, &headers, response);
|
416
343
|
if (!req->ended()) {
|
417
344
|
endRequest(&client, &req);
|
418
345
|
}
|
419
346
|
} else {
|
420
|
-
|
347
|
+
endAsBadRequest(&client, &req, "PID required");
|
421
348
|
}
|
422
349
|
}
|
423
350
|
|
@@ -433,8 +360,8 @@ private:
|
|
433
360
|
|
434
361
|
void processGc(Client *client, Request *req) {
|
435
362
|
if (req->method != HTTP_PUT) {
|
436
|
-
|
437
|
-
} else if (
|
363
|
+
apiServerRespondWith405(this, client, req);
|
364
|
+
} else if (authorizeAdminOperation(this, client, req)) {
|
438
365
|
HeaderTable headers;
|
439
366
|
headers.insert(req->pool, "Content-Type", "application/json");
|
440
367
|
for (unsigned int i = 0; i < requestHandlers.size(); i++) {
|
@@ -446,7 +373,7 @@ private:
|
|
446
373
|
endRequest(&client, &req);
|
447
374
|
}
|
448
375
|
} else {
|
449
|
-
|
376
|
+
apiServerRespondWith401(this, client, req);
|
450
377
|
}
|
451
378
|
}
|
452
379
|
|
@@ -456,8 +383,8 @@ private:
|
|
456
383
|
|
457
384
|
void processConfig(Client *client, Request *req) {
|
458
385
|
if (req->method == HTTP_GET) {
|
459
|
-
if (!
|
460
|
-
|
386
|
+
if (!authorizeStateInspectionOperation(this, client, req)) {
|
387
|
+
apiServerRespondWith401(this, client, req);
|
461
388
|
}
|
462
389
|
|
463
390
|
HeaderTable headers;
|
@@ -482,14 +409,14 @@ private:
|
|
482
409
|
endRequest(&client, &req);
|
483
410
|
}
|
484
411
|
} else if (req->method == HTTP_PUT) {
|
485
|
-
if (!
|
486
|
-
|
412
|
+
if (!authorizeAdminOperation(this, client, req)) {
|
413
|
+
apiServerRespondWith401(this, client, req);
|
487
414
|
} else if (!req->hasBody()) {
|
488
415
|
endAsBadRequest(&client, &req, "Body required");
|
489
416
|
}
|
490
417
|
// Continue in processConfigBody()
|
491
418
|
} else {
|
492
|
-
|
419
|
+
apiServerRespondWith405(this, client, req);
|
493
420
|
}
|
494
421
|
}
|
495
422
|
|
@@ -550,67 +477,6 @@ private:
|
|
550
477
|
}
|
551
478
|
}
|
552
479
|
|
553
|
-
void processReopenLogs(Client *client, Request *req) {
|
554
|
-
if (req->method != HTTP_POST) {
|
555
|
-
respondWith405(client, req);
|
556
|
-
} else if (authorize(client, req, FULL)) {
|
557
|
-
int e;
|
558
|
-
HeaderTable headers;
|
559
|
-
headers.insert(req->pool, "Content-Type", "application/json");
|
560
|
-
|
561
|
-
string logFile = getLogFile();
|
562
|
-
if (logFile.empty()) {
|
563
|
-
writeSimpleResponse(client, 500, &headers, "{ \"status\": \"error\", "
|
564
|
-
"\"code\": \"NO_LOG_FILE\", "
|
565
|
-
"\"message\": \"" PROGRAM_NAME " was not configured with a log file.\" }\n");
|
566
|
-
if (!req->ended()) {
|
567
|
-
endRequest(&client, &req);
|
568
|
-
}
|
569
|
-
return;
|
570
|
-
}
|
571
|
-
|
572
|
-
if (!setLogFile(logFile, &e)) {
|
573
|
-
unsigned int bufsize = 1024;
|
574
|
-
char *message = (char *) psg_pnalloc(req->pool, bufsize);
|
575
|
-
snprintf(message, bufsize, "{ \"status\": \"error\", "
|
576
|
-
"\"code\": \"LOG_FILE_OPEN_ERROR\", "
|
577
|
-
"\"message\": \"Cannot reopen log file %s: %s (errno=%d)\" }",
|
578
|
-
logFile.c_str(), strerror(e), e);
|
579
|
-
writeSimpleResponse(client, 500, &headers, message);
|
580
|
-
if (!req->ended()) {
|
581
|
-
endRequest(&client, &req);
|
582
|
-
}
|
583
|
-
return;
|
584
|
-
}
|
585
|
-
P_NOTICE("Log file reopened.");
|
586
|
-
|
587
|
-
if (hasFileDescriptorLogFile()) {
|
588
|
-
if (!setFileDescriptorLogFile(getFileDescriptorLogFile(), &e)) {
|
589
|
-
unsigned int bufsize = 1024;
|
590
|
-
char *message = (char *) psg_pnalloc(req->pool, bufsize);
|
591
|
-
snprintf(message, bufsize, "{ \"status\": \"error\", "
|
592
|
-
"\"code\": \"FD_LOG_FILE_OPEN_ERROR\", "
|
593
|
-
"\"message\": \"Cannot reopen file descriptor log file %s: %s (errno=%d)\" }",
|
594
|
-
getFileDescriptorLogFile().c_str(), strerror(e), e);
|
595
|
-
writeSimpleResponse(client, 500, &headers, message);
|
596
|
-
if (!req->ended()) {
|
597
|
-
endRequest(&client, &req);
|
598
|
-
}
|
599
|
-
return;
|
600
|
-
}
|
601
|
-
P_NOTICE("File descriptor log file reopened.");
|
602
|
-
}
|
603
|
-
|
604
|
-
writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }\n");
|
605
|
-
|
606
|
-
if (!req->ended()) {
|
607
|
-
endRequest(&client, &req);
|
608
|
-
}
|
609
|
-
} else {
|
610
|
-
respondWith401(client, req);
|
611
|
-
}
|
612
|
-
}
|
613
|
-
|
614
480
|
bool requestBodyExceedsLimit(Client *client, Request *req, unsigned int limit = 1024 * 128) {
|
615
481
|
return (req->bodyType == Request::RBT_CONTENT_LENGTH
|
616
482
|
&& req->aux.bodyInfo.contentLength > limit)
|
@@ -618,92 +484,20 @@ private:
|
|
618
484
|
&& req->body.size() > limit);
|
619
485
|
}
|
620
486
|
|
621
|
-
void respondWith401(Client *client, Request *req) {
|
622
|
-
HeaderTable headers;
|
623
|
-
headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
|
624
|
-
headers.insert(req->pool, "WWW-Authenticate", "Basic realm=\"admin\"");
|
625
|
-
writeSimpleResponse(client, 401, &headers, "Unauthorized");
|
626
|
-
if (!req->ended()) {
|
627
|
-
endRequest(&client, &req);
|
628
|
-
}
|
629
|
-
}
|
630
|
-
|
631
|
-
void respondWith404(Client *client, Request *req) {
|
632
|
-
HeaderTable headers;
|
633
|
-
headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
|
634
|
-
writeSimpleResponse(client, 404, &headers, "Not found");
|
635
|
-
if (!req->ended()) {
|
636
|
-
endRequest(&client, &req);
|
637
|
-
}
|
638
|
-
}
|
639
|
-
|
640
|
-
void respondWith405(Client *client, Request *req) {
|
641
|
-
HeaderTable headers;
|
642
|
-
headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
|
643
|
-
writeSimpleResponse(client, 405, &headers, "Method not allowed");
|
644
|
-
if (!req->ended()) {
|
645
|
-
endRequest(&client, &req);
|
646
|
-
}
|
647
|
-
}
|
648
|
-
|
649
|
-
void respondWith413(Client *client, Request *req) {
|
650
|
-
HeaderTable headers;
|
651
|
-
headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
|
652
|
-
writeSimpleResponse(client, 413, &headers, "Request body too large");
|
653
|
-
if (!req->ended()) {
|
654
|
-
endRequest(&client, &req);
|
655
|
-
}
|
656
|
-
}
|
657
|
-
|
658
|
-
void respondWith422(Client *client, Request *req, const StaticString &body) {
|
659
|
-
HeaderTable headers;
|
660
|
-
headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
|
661
|
-
headers.insert(req->pool, "Content-Type", "text/plain; charset=utf-8");
|
662
|
-
writeSimpleResponse(client, 422, &headers, body);
|
663
|
-
if (!req->ended()) {
|
664
|
-
endRequest(&client, &req);
|
665
|
-
}
|
666
|
-
}
|
667
|
-
|
668
487
|
protected:
|
669
488
|
virtual void onRequestBegin(Client *client, Request *req) {
|
670
489
|
TRACE_POINT();
|
671
490
|
StaticString path = req->getPathWithoutQueryString();
|
672
491
|
|
673
|
-
P_INFO("
|
492
|
+
P_INFO("API request: " << http_method_str(req->method) <<
|
674
493
|
" " << StaticString(req->path.start->data, req->path.size));
|
675
494
|
|
676
495
|
try {
|
677
|
-
|
678
|
-
processServerStatus(client, req);
|
679
|
-
} else if (regex_match(path, serverConnectionPath)) {
|
680
|
-
processServerConnectionOperation(client, req);
|
681
|
-
} else if (path == P_STATIC_STRING("/pool.xml")) {
|
682
|
-
processPoolStatusXml(client, req);
|
683
|
-
} else if (path == P_STATIC_STRING("/pool.txt")) {
|
684
|
-
processPoolStatusTxt(client, req);
|
685
|
-
} else if (path == P_STATIC_STRING("/pool/restart_app_group.json")) {
|
686
|
-
processPoolRestartAppGroup(client, req);
|
687
|
-
} else if (path == P_STATIC_STRING("/pool/detach_process.json")) {
|
688
|
-
processPoolDetachProcess(client, req);
|
689
|
-
} else if (path == P_STATIC_STRING("/backtraces.txt")) {
|
690
|
-
processBacktraces(client, req);
|
691
|
-
} else if (path == P_STATIC_STRING("/ping.json")) {
|
692
|
-
processPing(client, req);
|
693
|
-
} else if (path == P_STATIC_STRING("/shutdown.json")) {
|
694
|
-
processShutdown(client, req);
|
695
|
-
} else if (path == P_STATIC_STRING("/gc.json")) {
|
696
|
-
processGc(client, req);
|
697
|
-
} else if (path == P_STATIC_STRING("/config.json")) {
|
698
|
-
processConfig(client, req);
|
699
|
-
} else if (path == P_STATIC_STRING("/reopen_logs.json")) {
|
700
|
-
processReopenLogs(client, req);
|
701
|
-
} else {
|
702
|
-
respondWith404(client, req);
|
703
|
-
}
|
496
|
+
route(client, req, path);
|
704
497
|
} catch (const oxt::tracable_exception &e) {
|
705
498
|
SKC_ERROR(client, "Exception: " << e.what() << "\n" << e.backtrace());
|
706
499
|
if (!req->ended()) {
|
500
|
+
req->wantKeepAlive = false;
|
707
501
|
endRequest(&client, &req);
|
708
502
|
}
|
709
503
|
}
|
@@ -717,7 +511,7 @@ protected:
|
|
717
511
|
// Data
|
718
512
|
req->body.append(buffer.start, buffer.size());
|
719
513
|
if (requestBodyExceedsLimit(client, req)) {
|
720
|
-
|
514
|
+
apiServerRespondWith413(this, client, req);
|
721
515
|
}
|
722
516
|
} else if (errcode == 0) {
|
723
517
|
// EOF
|
@@ -737,11 +531,12 @@ protected:
|
|
737
531
|
} catch (const oxt::tracable_exception &e) {
|
738
532
|
SKC_ERROR(client, "Exception: " << e.what() << "\n" << e.backtrace());
|
739
533
|
if (!req->ended()) {
|
534
|
+
req->wantKeepAlive = false;
|
740
535
|
endRequest(&client, &req);
|
741
536
|
}
|
742
537
|
}
|
743
538
|
} else {
|
744
|
-
|
539
|
+
apiServerRespondWith422(this, client, req, reader.getFormattedErrorMessages());
|
745
540
|
}
|
746
541
|
} else {
|
747
542
|
// Error
|
@@ -755,42 +550,45 @@ protected:
|
|
755
550
|
if (!req->jsonBody.isNull()) {
|
756
551
|
req->jsonBody = Json::Value();
|
757
552
|
}
|
553
|
+
req->authorization = Authorization();
|
758
554
|
ParentClass::deinitializeRequest(client, req);
|
759
555
|
}
|
760
556
|
|
761
|
-
virtual unsigned int getClientName(const Client *client, char *buf, size_t size) const {
|
762
|
-
char *pos = buf;
|
763
|
-
const char *end = buf + size - 1;
|
764
|
-
pos = appendData(pos, end, "Adm.", 1);
|
765
|
-
pos += uintToString(client->number, pos, end - pos);
|
766
|
-
*pos = '\0';
|
767
|
-
return pos - buf;
|
768
|
-
}
|
769
|
-
|
770
557
|
public:
|
771
558
|
vector<RequestHandler *> requestHandlers;
|
559
|
+
ApiAccountDatabase *apiAccountDatabase;
|
772
560
|
ApplicationPool2::PoolPtr appPool;
|
561
|
+
string instanceDir;
|
562
|
+
string fdPassingPassword;
|
773
563
|
EventFd *exitEvent;
|
774
564
|
vector<Authorization> authorizations;
|
775
565
|
|
776
|
-
|
566
|
+
ApiServer(ServerKit::Context *context)
|
777
567
|
: ParentClass(context),
|
778
568
|
serverConnectionPath("^/server/(.+)\\.json$"),
|
569
|
+
apiAccountDatabase(NULL),
|
779
570
|
exitEvent(NULL)
|
780
571
|
{ }
|
781
572
|
|
782
573
|
virtual StaticString getServerName() const {
|
783
|
-
return P_STATIC_STRING("
|
574
|
+
return P_STATIC_STRING("ApiServer");
|
784
575
|
}
|
785
576
|
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
577
|
+
virtual unsigned int getClientName(const Client *client, char *buf, size_t size) const {
|
578
|
+
char *pos = buf;
|
579
|
+
const char *end = buf + size - 1;
|
580
|
+
pos = appendData(pos, end, "Adm.", 1);
|
581
|
+
pos += uintToString(client->number, pos, end - pos);
|
582
|
+
*pos = '\0';
|
583
|
+
return pos - buf;
|
584
|
+
}
|
585
|
+
|
586
|
+
bool authorizeByUid(uid_t uid) const {
|
587
|
+
return appPool->authorizeByUid(uid);
|
588
|
+
}
|
589
|
+
|
590
|
+
bool authorizeByApiKey(const ApplicationPool2::ApiKey &apiKey) const {
|
591
|
+
return appPool->authorizeByApiKey(apiKey);
|
794
592
|
}
|
795
593
|
};
|
796
594
|
|
@@ -798,4 +596,4 @@ public:
|
|
798
596
|
} // namespace ServerAgent
|
799
597
|
} // namespace Passenger
|
800
598
|
|
801
|
-
#endif /*
|
599
|
+
#endif /* _PASSENGER_SERVER_AGENT_API_SERVER_H_ */
|