passenger 5.1.7 → 5.1.8

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.

Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +13 -2
  3. data/CONTRIBUTING.md +1 -1
  4. data/build/agent.rb +1 -1
  5. data/build/cxx_tests.rb +6 -0
  6. data/build/support/cxx_dependency_map.rb +1286 -391
  7. data/build/support/general.rb +0 -26
  8. data/resources/templates/standalone/rails_asset_pipeline.erb +2 -2
  9. data/src/agent/Core/ApiServer.h +49 -44
  10. data/src/agent/Core/ApplicationPool/Pool.h +1 -1
  11. data/src/agent/Core/ApplicationPool/Process.h +1 -1
  12. data/src/agent/Core/ApplicationPool/Socket.h +1 -1
  13. data/src/agent/Core/Controller.h +16 -8
  14. data/src/agent/Core/Controller/CheckoutSession.cpp +1 -1
  15. data/src/agent/Core/Controller/Config.cpp +68 -0
  16. data/src/agent/Core/Controller/Config.h +70 -34
  17. data/src/agent/Core/Controller/ForwardResponse.cpp +5 -5
  18. data/src/agent/Core/Controller/Hooks.cpp +5 -14
  19. data/src/agent/Core/Controller/Implementation.cpp +1 -1
  20. data/src/agent/Core/Controller/InitRequest.cpp +31 -29
  21. data/src/agent/Core/Controller/InitializationAndShutdown.cpp +4 -4
  22. data/src/agent/Core/Controller/InternalUtils.cpp +3 -3
  23. data/src/agent/Core/Controller/Miscellaneous.cpp +1 -1
  24. data/src/agent/Core/Controller/Request.h +2 -2
  25. data/src/agent/Core/Controller/SendRequest.cpp +5 -5
  26. data/src/agent/Core/Controller/StateInspection.cpp +1 -1
  27. data/src/agent/Core/Controller/TurboCaching.h +2 -2
  28. data/src/agent/Core/CoreMain.cpp +2 -2
  29. data/src/agent/Core/ResponseCache.h +3 -3
  30. data/src/agent/Core/SpawningKit/BackgroundIOCapturer.h +3 -3
  31. data/src/agent/Core/SpawningKit/DirectSpawner.h +2 -2
  32. data/src/agent/Core/SpawningKit/PipeWatcher.h +3 -3
  33. data/src/agent/Core/SpawningKit/SmartSpawner.h +2 -2
  34. data/src/agent/Core/SpawningKit/Spawner.h +1 -1
  35. data/src/agent/Core/UnionStation/Connection.h +1 -1
  36. data/src/agent/Core/UnionStation/Context.h +1 -1
  37. data/src/agent/Core/UnionStation/Transaction.h +1 -1
  38. data/src/agent/Shared/ApiServerUtils.h +73 -27
  39. data/src/agent/Shared/Base.cpp +61 -73
  40. data/src/agent/UstRouter/ApiServer.h +34 -45
  41. data/src/agent/UstRouter/Controller.h +86 -60
  42. data/src/agent/UstRouter/RemoteSender.h +1 -1
  43. data/src/agent/UstRouter/RemoteSink.h +1 -1
  44. data/src/agent/Watchdog/ApiServer.h +42 -50
  45. data/src/agent/Watchdog/WatchdogMain.cpp +1 -1
  46. data/src/apache2_module/Configuration.hpp +1 -1
  47. data/src/apache2_module/Hooks.cpp +27 -13
  48. data/src/cxx_supportlib/AppTypes.h +1 -1
  49. data/src/cxx_supportlib/BackgroundEventLoop.cpp +1 -1
  50. data/src/cxx_supportlib/ConfigKit/AsyncUtils.h +86 -0
  51. data/src/cxx_supportlib/ConfigKit/Common.h +6 -3
  52. data/src/cxx_supportlib/ConfigKit/IN_PRACTICE.md +1039 -0
  53. data/src/cxx_supportlib/ConfigKit/README.md +112 -497
  54. data/src/cxx_supportlib/ConfigKit/Schema.h +78 -15
  55. data/src/cxx_supportlib/ConfigKit/Store.h +272 -53
  56. data/src/cxx_supportlib/ConfigKit/SubComponentUtils.h +59 -0
  57. data/src/cxx_supportlib/ConfigKit/Utils.h +26 -65
  58. data/src/cxx_supportlib/ConfigKit/ValidationUtils.h +69 -0
  59. data/src/cxx_supportlib/ConfigKit/VariantMapUtils.h +7 -4
  60. data/src/cxx_supportlib/Constants.h +4 -1
  61. data/src/cxx_supportlib/Crypto.cpp +1 -1
  62. data/src/cxx_supportlib/DataStructures/StringKeyTable.h +26 -7
  63. data/src/cxx_supportlib/FileDescriptor.h +1 -1
  64. data/src/cxx_supportlib/Hooks.h +1 -1
  65. data/src/cxx_supportlib/LoggingKit/Assert.h +130 -0
  66. data/src/cxx_supportlib/LoggingKit/Config.h +97 -0
  67. data/src/cxx_supportlib/LoggingKit/Context.h +94 -0
  68. data/src/cxx_supportlib/LoggingKit/Forward.h +95 -0
  69. data/src/cxx_supportlib/LoggingKit/Implementation.cpp +695 -0
  70. data/src/cxx_supportlib/LoggingKit/Logging.h +204 -0
  71. data/src/cxx_supportlib/LoggingKit/LoggingKit.h +33 -0
  72. data/src/cxx_supportlib/LveLoggingDecorator.h +1 -1
  73. data/src/cxx_supportlib/MemoryKit/mbuf.cpp +1 -1
  74. data/src/cxx_supportlib/RandomGenerator.h +1 -1
  75. data/src/cxx_supportlib/SafeLibev.h +1 -1
  76. data/src/cxx_supportlib/ServerKit/AcceptLoadBalancer.h +1 -1
  77. data/src/cxx_supportlib/ServerKit/Channel.h +1 -1
  78. data/src/cxx_supportlib/ServerKit/FileBufferedChannel.h +1 -1
  79. data/src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h +1 -1
  80. data/src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h +1 -1
  81. data/src/cxx_supportlib/ServerKit/HttpHeaderParser.h +1 -1
  82. data/src/cxx_supportlib/ServerKit/HttpServer.h +48 -15
  83. data/src/cxx_supportlib/ServerKit/Server.h +79 -52
  84. data/src/cxx_supportlib/StaticString.h +12 -0
  85. data/src/cxx_supportlib/Utils/Curl.h +16 -0
  86. data/src/cxx_supportlib/Utils/FastStringStream.h +6 -1
  87. data/src/cxx_supportlib/Utils/ScopeGuard.h +1 -1
  88. data/src/cxx_supportlib/Utils/StrIntUtils.cpp +2 -19
  89. data/src/cxx_supportlib/WatchdogLauncher.h +3 -2
  90. data/src/ruby_supportlib/phusion_passenger.rb +3 -3
  91. data/src/ruby_supportlib/phusion_passenger/common_library.rb +12 -12
  92. data/src/ruby_supportlib/phusion_passenger/constants.rb +6 -3
  93. data/src/ruby_supportlib/phusion_passenger/standalone/start_command.rb +1 -0
  94. data/src/ruby_supportlib/phusion_passenger/standalone/stop_command.rb +1 -0
  95. metadata +14 -4
  96. data/src/cxx_supportlib/Logging.cpp +0 -295
  97. data/src/cxx_supportlib/Logging.h +0 -385
@@ -35,7 +35,7 @@
35
35
  #include <jsoncpp/json.h>
36
36
 
37
37
  #include <Exceptions.h>
38
- #include <Logging.h>
38
+ #include <LoggingKit/LoggingKit.h>
39
39
  #include <ConfigKit/Common.h>
40
40
  #include <ConfigKit/DummyTranslator.h>
41
41
  #include <ConfigKit/Utils.h>
@@ -59,16 +59,19 @@ public:
59
59
  Type type;
60
60
  Flags flags;
61
61
  ValueGetter defaultValueGetter;
62
+ ValueFilter inspectFilter;
62
63
 
63
64
  Entry()
64
65
  : type(UNKNOWN_TYPE),
65
66
  flags(OPTIONAL)
66
67
  { }
67
68
 
68
- Entry(Type _type, Flags _flags, const ValueGetter &_defaultValueGetter)
69
+ Entry(Type _type, Flags _flags, const ValueGetter &_defaultValueGetter,
70
+ const ValueFilter &_inspectFilter)
69
71
  : type(_type),
70
72
  flags(_flags),
71
- defaultValueGetter(_defaultValueGetter)
73
+ defaultValueGetter(_defaultValueGetter),
74
+ inspectFilter(_inspectFilter)
72
75
  { }
73
76
 
74
77
  Json::Value inspect() const {
@@ -85,18 +88,43 @@ public:
85
88
  if (flags & READ_ONLY) {
86
89
  doc["read_only"] = true;
87
90
  }
91
+ if (flags & SECRET) {
92
+ doc["secret"] = true;
93
+ }
88
94
  if (defaultValueGetter) {
89
- doc["has_default_value"] = true;
95
+ if (flags & _DYNAMIC_DEFAULT_VALUE) {
96
+ doc["has_default_value"] = "dynamic";
97
+ } else {
98
+ doc["has_default_value"] = "static";
99
+ doc["default_value"] = Schema::getStaticDefaultValue(*this);
100
+ }
90
101
  }
91
102
  }
92
103
  };
93
104
 
105
+ class EntryBuilder {
106
+ private:
107
+ Entry *entry;
108
+
109
+ public:
110
+ EntryBuilder(Entry &_entry)
111
+ : entry(&_entry)
112
+ { }
113
+
114
+ EntryBuilder &setInspectFilter(const ValueFilter &filter) {
115
+ entry->inspectFilter = filter;
116
+ return *this;
117
+ }
118
+ };
119
+
94
120
  typedef StringKeyTable<Entry>::ConstIterator ConstIterator;
95
121
  typedef boost::function<void (const Store &store, vector<Error> &errors)> Validator;
122
+ typedef boost::function<Json::Value (const Json::Value &effectiveValues)> Normalizer;
96
123
 
97
124
  private:
98
125
  StringKeyTable<Entry> entries;
99
126
  boost::container::vector<Validator> validators;
127
+ boost::container::vector<Normalizer> normalizers;
100
128
  bool finalized;
101
129
 
102
130
  static Json::Value returnJsonValue(const Store &store, Json::Value v) {
@@ -114,6 +142,13 @@ private:
114
142
  const Schema *subschema, const Translator *translator,
115
143
  const Validator &origValidator);
116
144
 
145
+ template<typename Translator>
146
+ static Json::Value normalizeSubSchema(const Json::Value &effectiveValues,
147
+ const Schema *mainSchema, const Schema *subschema,
148
+ const Translator *translator, const Normalizer &origNormalizer);
149
+
150
+ static Json::Value getStaticDefaultValue(const Schema::Entry &entry);
151
+
117
152
  public:
118
153
  Schema()
119
154
  : finalized(false)
@@ -122,28 +157,29 @@ public:
122
157
  /**
123
158
  * Register a new schema entry, possibly with a static default value.
124
159
  */
125
- void add(const HashedStaticString &key, Type type, unsigned int flags,
160
+ EntryBuilder add(const HashedStaticString &key, Type type, unsigned int flags,
126
161
  const Json::Value &defaultValue = Json::Value(Json::nullValue))
127
162
  {
128
163
  assert(!finalized);
129
164
  if (defaultValue.isNull()) {
130
- Entry entry(type, (Flags) flags, ValueGetter());
131
- entries.insert(key, entry);
165
+ Entry entry(type, (Flags) flags, ValueGetter(), ValueFilter());
166
+ return EntryBuilder(entries.insert(key, entry)->value);
132
167
  } else {
133
168
  if (flags & REQUIRED) {
134
169
  throw ArgumentException(
135
170
  "A key cannot be required and have a default value at the same time");
136
171
  }
137
172
  Entry entry(type, (Flags) flags,
138
- boost::bind(returnJsonValue, boost::placeholders::_1, defaultValue));
139
- entries.insert(key, entry);
173
+ boost::bind(returnJsonValue, boost::placeholders::_1, defaultValue),
174
+ ValueFilter());
175
+ return EntryBuilder(entries.insert(key, entry)->value);
140
176
  }
141
177
  }
142
178
 
143
179
  /**
144
180
  * Register a new schema entry with a dynamic default value.
145
181
  */
146
- void addWithDynamicDefault(const HashedStaticString &key, Type type, unsigned int flags,
182
+ EntryBuilder addWithDynamicDefault(const HashedStaticString &key, Type type, unsigned int flags,
147
183
  const ValueGetter &defaultValueGetter)
148
184
  {
149
185
  if (flags & REQUIRED) {
@@ -151,8 +187,9 @@ public:
151
187
  "A key cannot be required and have a default value at the same time");
152
188
  }
153
189
  assert(!finalized);
154
- Entry entry(type, (Flags) (flags | _DYNAMIC_DEFAULT_VALUE), defaultValueGetter);
155
- entries.insert(key, entry);
190
+ Entry entry(type, (Flags) (flags | _DYNAMIC_DEFAULT_VALUE), defaultValueGetter,
191
+ ValueFilter());
192
+ return EntryBuilder(entries.insert(key, entry)->value);
156
193
  }
157
194
 
158
195
  void addSubSchema(const Schema &subschema) {
@@ -182,18 +219,25 @@ public:
182
219
  }
183
220
 
184
221
  Entry entry2(entry.type, (Flags) (entry.flags | _FROM_SUBSCHEMA),
185
- valueGetter);
222
+ valueGetter, entry.inspectFilter);
186
223
  entries.insert(translator.reverseTranslateOne(key), entry2);
187
224
  it.next();
188
225
  }
189
226
 
190
- boost::container::vector<Schema::Validator>::const_iterator v_it, v_end
227
+ boost::container::vector<Validator>::const_iterator v_it, v_end
191
228
  = subschema.getValidators().end();
192
229
  for (v_it = subschema.getValidators().begin(); v_it != v_end; v_it++) {
193
230
  validators.push_back(boost::bind(validateSubSchema<Translator>,
194
231
  boost::placeholders::_1, boost::placeholders::_2,
195
232
  &subschema, &translator, *v_it));
196
233
  }
234
+
235
+ boost::container::vector<Normalizer>::const_iterator n_it, n_end
236
+ = subschema.getNormalizers().end();
237
+ for (n_it = subschema.getNormalizers().begin(); n_it != n_end; n_it++) {
238
+ normalizers.push_back(boost::bind(normalizeSubSchema<Translator>,
239
+ boost::placeholders::_1, this, &subschema, &translator, *n_it));
240
+ }
197
241
  }
198
242
 
199
243
  void addValidator(const Validator &validator) {
@@ -201,11 +245,17 @@ public:
201
245
  validators.push_back(validator);
202
246
  }
203
247
 
248
+ void addNormalizer(const Normalizer &normalizer) {
249
+ assert(!finalized);
250
+ normalizers.push_back(normalizer);
251
+ }
252
+
204
253
  void finalize() {
205
254
  assert(!finalized);
206
255
  entries.compact();
207
256
  finalized = true;
208
257
  validators.shrink_to_fit();
258
+ normalizers.shrink_to_fit();
209
259
  }
210
260
 
211
261
  bool get(const HashedStaticString &key, const Entry **entry) const {
@@ -241,7 +291,6 @@ public:
241
291
 
242
292
  switch (entry->type) {
243
293
  case STRING_TYPE:
244
- case PASSWORD_TYPE:
245
294
  if (value.isConvertibleTo(Json::stringValue)) {
246
295
  return true;
247
296
  } else {
@@ -302,6 +351,15 @@ public:
302
351
  error = Error("'{{" + key + "}}' must be an array");
303
352
  return false;
304
353
  }
354
+ case OBJECT_TYPE:
355
+ if (value.isObject()) {
356
+ return true;
357
+ } else {
358
+ error = Error("'{{" + key + "}}' must be a JSON object");
359
+ return false;
360
+ }
361
+ case ANY_TYPE:
362
+ return true;
305
363
  default:
306
364
  P_BUG("Unknown type " + Passenger::toString((int) entry->type));
307
365
  return false;
@@ -313,6 +371,11 @@ public:
313
371
  return validators;
314
372
  }
315
373
 
374
+ const boost::container::vector<Normalizer> &getNormalizers() const {
375
+ assert(finalized);
376
+ return normalizers;
377
+ }
378
+
316
379
  ConstIterator getIterator() const {
317
380
  assert(finalized);
318
381
  return ConstIterator(entries);
@@ -28,12 +28,24 @@
28
28
 
29
29
  #include <string>
30
30
  #include <vector>
31
+ #include <cassert>
32
+ // for std::swap()
33
+ #if __cplusplus >= 201103L
34
+ #include <utility>
35
+ #else
36
+ #include <algorithm>
37
+ #endif
38
+ #include <boost/config.hpp>
39
+
40
+ #include <jsoncpp/json.h>
31
41
 
32
42
  #include <ConfigKit/Common.h>
33
43
  #include <ConfigKit/Schema.h>
34
44
  #include <ConfigKit/Utils.h>
35
- #include <jsoncpp/json.h>
45
+ #include <LoggingKit/Assert.h>
46
+ #include <Exceptions.h>
36
47
  #include <DataStructures/StringKeyTable.h>
48
+ #include <Utils/StrIntUtils.h>
37
49
 
38
50
  namespace Passenger {
39
51
  namespace ConfigKit {
@@ -87,7 +99,7 @@ private:
87
99
  }
88
100
  };
89
101
 
90
- const Schema &schema;
102
+ const Schema *schema;
91
103
  StringKeyTable<Entry> entries;
92
104
  bool updatedOnce;
93
105
 
@@ -101,8 +113,8 @@ private:
101
113
  }
102
114
  }
103
115
 
104
- static Json::Value maybeFilterPassword(const Entry &entry, const Json::Value &value) {
105
- if (entry.schemaEntry->type == PASSWORD_TYPE) {
116
+ static Json::Value maybeFilterSecret(const Entry &entry, const Json::Value &value) {
117
+ if (entry.schemaEntry->flags & SECRET) {
106
118
  if (value.isNull()) {
107
119
  return Json::nullValue;
108
120
  } else {
@@ -113,12 +125,24 @@ private:
113
125
  }
114
126
  }
115
127
 
128
+ void initialize() {
129
+ Schema::ConstIterator it = schema->getIterator();
130
+
131
+ while (*it != NULL) {
132
+ Entry entry(it.getValue());
133
+ entries.insert(it.getKey(), entry);
134
+ it.next();
135
+ }
136
+
137
+ entries.compact();
138
+ }
139
+
116
140
  bool isWritable(const Entry &entry) const {
117
141
  return !(entry.schemaEntry->flags & READ_ONLY) || !updatedOnce;
118
142
  }
119
143
 
120
144
  void applyCustomValidators(const Json::Value &updates, vector<Error> &errors) const {
121
- Store tempStore(schema);
145
+ Store tempStore(*schema);
122
146
  StringKeyTable<Entry>::Iterator it(tempStore.entries);
123
147
 
124
148
  while (*it != NULL) {
@@ -133,31 +157,157 @@ private:
133
157
  }
134
158
 
135
159
  boost::container::vector<Schema::Validator>::const_iterator v_it, v_end
136
- = schema.getValidators().end();
137
- for (v_it = schema.getValidators().begin(); v_it != v_end; v_it++) {
160
+ = schema->getValidators().end();
161
+ for (v_it = schema->getValidators().begin(); v_it != v_end; v_it++) {
138
162
  const Schema::Validator &validator = *v_it;
139
163
  validator(tempStore, errors);
140
164
  }
141
165
  }
142
166
 
167
+ void applyNormalizers(Json::Value &doc) const {
168
+ boost::container::vector<Schema::Normalizer>::const_iterator n_it, n_end;
169
+
170
+ n_it = schema->getNormalizers().begin();
171
+ n_end = schema->getNormalizers().end();
172
+ for (; n_it != n_end; n_it++) {
173
+ const Schema::Normalizer &normalizer = *n_it;
174
+ Json::Value effectiveValues(Json::objectValue);
175
+ Json::Value::iterator it, end = doc.end();
176
+
177
+ for (it = doc.begin(); it != end; it++) {
178
+ string name = it.name();
179
+ effectiveValues[name] = doc[name]["effective_value"];
180
+ }
181
+
182
+ Json::Value updates = normalizer(effectiveValues);
183
+ if (OXT_UNLIKELY(!updates.isNull() && !updates.isObject())) {
184
+ P_BUG("ConfigKit normalizers may only return null or object values");
185
+ }
186
+ if (updates.isNull() || updates.empty()) {
187
+ continue;
188
+ }
189
+
190
+ end = updates.end();
191
+ for (it = updates.begin(); it != end; it++) {
192
+ string name = it.name();
193
+ if (doc.isMember(name)) {
194
+ Json::Value &subdoc = doc[name];
195
+ subdoc["user_value"] = *it;
196
+ subdoc["effective_value"] = *it;
197
+ } else {
198
+ P_BUG("A ConfigKit normalizer returned a key that is not part of the schema: "
199
+ << name);
200
+ }
201
+ }
202
+ }
203
+ }
204
+
205
+ void applyInspectFilters(Json::Value &doc) const {
206
+ StringKeyTable<Entry>::ConstIterator it(entries);
207
+ while (*it != NULL) {
208
+ const Entry &entry = it.getValue();
209
+ if (entry.schemaEntry->inspectFilter == NULL) {
210
+ it.next();
211
+ continue;
212
+ }
213
+
214
+ const HashedStaticString &key = it.getKey();
215
+ Json::Value &subdoc = doc[key];
216
+
217
+ Json::Value &userValue = subdoc["user_value"];
218
+ userValue = entry.schemaEntry->inspectFilter(userValue);
219
+
220
+ if (subdoc.isMember("default_value")) {
221
+ Json::Value &defaultValue = subdoc["default_value"];
222
+ defaultValue = entry.schemaEntry->inspectFilter(defaultValue);
223
+ }
224
+
225
+ Json::Value &effectiveValue = subdoc["effective_value"];
226
+ effectiveValue = entry.schemaEntry->inspectFilter(effectiveValue);
227
+
228
+ it.next();
229
+ }
230
+ }
231
+
232
+ void doFilterSecrets(Json::Value &doc) const {
233
+ StringKeyTable<Entry>::ConstIterator it(entries);
234
+ while (*it != NULL) {
235
+ const HashedStaticString &key = it.getKey();
236
+ const Entry &entry = it.getValue();
237
+ Json::Value &subdoc = doc[key];
238
+
239
+ Json::Value &userValue = subdoc["user_value"];
240
+ userValue = maybeFilterSecret(entry, userValue);
241
+
242
+ if (subdoc.isMember("default_value")) {
243
+ Json::Value &defaultValue = subdoc["default_value"];
244
+ defaultValue = maybeFilterSecret(entry, defaultValue);
245
+ }
246
+
247
+ Json::Value &effectiveValue = subdoc["effective_value"];
248
+ effectiveValue = maybeFilterSecret(entry, effectiveValue);
249
+
250
+ it.next();
251
+ }
252
+ }
253
+
143
254
  public:
255
+ Store()
256
+ : schema(NULL),
257
+ entries(0, 0),
258
+ updatedOnce(false)
259
+ { }
260
+
144
261
  Store(const Schema &_schema)
145
- : schema(_schema),
262
+ : schema(&_schema),
146
263
  updatedOnce(false)
147
264
  {
148
- Schema::ConstIterator it = _schema.getIterator();
265
+ initialize();
266
+ }
149
267
 
150
- while (*it != NULL) {
151
- Entry entry(it.getValue());
152
- entries.insert(it.getKey(), entry);
153
- it.next();
268
+ Store(const Schema &_schema, const Json::Value &initialValues)
269
+ : schema(&_schema),
270
+ updatedOnce(false)
271
+ {
272
+ vector<Error> errors;
273
+ initialize();
274
+ if (!update(initialValues, errors)) {
275
+ throw ArgumentException("Invalid initial configuration: "
276
+ + toString(errors));
154
277
  }
278
+ }
155
279
 
156
- entries.compact();
280
+ template<typename Translator>
281
+ Store(const Schema &_schema, const Json::Value &initialValues,
282
+ const Translator &translator)
283
+ : schema(&_schema),
284
+ updatedOnce(false)
285
+ {
286
+ vector<Error> errors;
287
+ initialize();
288
+ if (!update(translator.translate(initialValues), errors)) {
289
+ errors = translator.reverseTranslate(errors);
290
+ throw ArgumentException("Invalid initial configuration: "
291
+ + toString(errors));
292
+ }
293
+ }
294
+
295
+ Store(const Store &other, const Json::Value &updates, vector<Error> &errors)
296
+ : schema(other.schema),
297
+ updatedOnce(false)
298
+ {
299
+ initialize();
300
+ if (update(other.inspectUserValues(), errors)) {
301
+ update(updates, errors);
302
+ }
157
303
  }
158
304
 
159
305
  const Schema &getSchema() const {
160
- return schema;
306
+ return *schema;
307
+ }
308
+
309
+ bool hasBeenUpdatedAtLeastOnce() const {
310
+ return updatedOnce;
161
311
  }
162
312
 
163
313
  /**
@@ -188,17 +338,22 @@ public:
188
338
  * and whether it passes validation, without actually updating the
189
339
  * stored configuration.
190
340
  *
191
- * You can use the `forceApplyUpdatePreview` method to apply the result, but
192
- * be sure to do that only if validation passes.
193
- *
194
341
  * If validation fails then any validation errors will be added to `errors`.
195
342
  *
196
343
  * Any keys in `updates` that are not registered are omitted from the result.
197
344
  * Any keys not in `updates` do not affect existing values stored in the store.
198
345
  *
199
- * The format returned by this method is the same as that of `dump()`.
346
+ * The format returned by this method is the same as that of `inspect()`,
347
+ * with the following exceptions:
348
+ *
349
+ * - If `filterSecrets` is set to false, values of fields
350
+ * marked with the `SECRET` flag are not filtered.
351
+ * - If `shouldApplyInspectFilters` is set to false, values of fields
352
+ * are not passed through inspect filters.
200
353
  */
201
- Json::Value previewUpdate(const Json::Value &updates, vector<Error> &errors) const {
354
+ Json::Value previewUpdate(const Json::Value &updates, vector<Error> &errors,
355
+ bool filterSecrets = true, bool shouldApplyInspectFilters = true) const
356
+ {
202
357
  if (!updates.isNull() && !updates.isObject()) {
203
358
  errors.push_back(Error("The JSON document must be an object"));
204
359
  return inspect();
@@ -206,6 +361,7 @@ public:
206
361
 
207
362
  Json::Value result(Json::objectValue);
208
363
  StringKeyTable<Entry>::ConstIterator it(entries);
364
+ vector<Error> tmpErrors;
209
365
  Error error;
210
366
 
211
367
  while (*it != NULL) {
@@ -213,6 +369,8 @@ public:
213
369
  const Entry &entry = it.getValue();
214
370
  Json::Value subdoc(Json::objectValue);
215
371
 
372
+ entry.schemaEntry->inspect(subdoc);
373
+
216
374
  if (isWritable(entry) && updates.isMember(key)) {
217
375
  subdoc["user_value"] = updates[key];
218
376
  } else {
@@ -226,41 +384,33 @@ public:
226
384
  subdoc["effective_value"] =
227
385
  getEffectiveValue(subdoc["user_value"],
228
386
  subdoc["default_value"]);
229
- if (!schema.validateValue(it.getKey(), effectiveValue, error)) {
230
- errors.push_back(error);
387
+ if (!schema->validateValue(it.getKey(), effectiveValue, error)) {
388
+ tmpErrors.push_back(error);
231
389
  }
232
390
 
233
- entry.schemaEntry->inspect(subdoc);
234
-
235
391
  result[it.getKey()] = subdoc;
236
392
  it.next();
237
393
  }
238
394
 
239
- if (!schema.getValidators().empty()) {
240
- applyCustomValidators(updates, errors);
395
+ if (!schema->getValidators().empty()) {
396
+ applyCustomValidators(updates, tmpErrors);
241
397
  }
242
398
 
243
- return result;
244
- }
399
+ if (tmpErrors.empty()) {
400
+ applyNormalizers(result);
401
+ }
245
402
 
246
- /**
247
- * Applies the result of `updatePreview()` without performing any
248
- * validation. Be sure to only call this if you've verified that
249
- * `updatePreview()` passes validation, otherwise you will end up
250
- * with invalid data in the store.
251
- */
252
- void forceApplyUpdatePreview(const Json::Value &preview) {
253
- StringKeyTable<Entry>::Iterator it(entries);
254
- while (*it != NULL) {
255
- Entry &entry = it.getValue();
256
- const Json::Value &subdoc =
257
- const_cast<const Json::Value &>(preview)[it.getKey()];
258
- if (isWritable(entry)) {
259
- entry.userValue = subdoc["user_value"];
260
- }
261
- it.next();
403
+ if (shouldApplyInspectFilters) {
404
+ applyInspectFilters(result);
262
405
  }
263
- updatedOnce = true;
406
+
407
+ if (filterSecrets) {
408
+ doFilterSecrets(result);
409
+ }
410
+
411
+ errors.insert(errors.end(), tmpErrors.begin(), tmpErrors.end());
412
+
413
+ return result;
264
414
  }
265
415
 
266
416
  /**
@@ -273,9 +423,19 @@ public:
273
423
  * Any keys not in `updates` do not affect existing values stored in the store.
274
424
  */
275
425
  bool update(const Json::Value &updates, vector<Error> &errors) {
276
- Json::Value preview = previewUpdate(updates, errors);
426
+ Json::Value preview = previewUpdate(updates, errors, false, false);
277
427
  if (errors.empty()) {
278
- forceApplyUpdatePreview(preview);
428
+ StringKeyTable<Entry>::Iterator it(entries);
429
+ while (*it != NULL) {
430
+ Entry &entry = it.getValue();
431
+ if (isWritable(entry)) {
432
+ const Json::Value &subdoc =
433
+ const_cast<const Json::Value &>(preview)[it.getKey()];
434
+ entry.userValue = subdoc["user_value"];
435
+ }
436
+ it.next();
437
+ }
438
+ updatedOnce = true;
279
439
  return true;
280
440
  } else {
281
441
  return false;
@@ -292,7 +452,7 @@ public:
292
452
  while (*it != NULL) {
293
453
  const HashedStaticString &subSchemaKey = it.getKey();
294
454
  Entry &subSchemaEntry = it.getValue();
295
- const StaticString mainSchemaKey = translator.reverseTranslateOne(
455
+ const string mainSchemaKey = translator.reverseTranslateOne(
296
456
  subSchemaKey);
297
457
  const Entry *mainSchemaEntry;
298
458
 
@@ -306,6 +466,12 @@ public:
306
466
  return result;
307
467
  }
308
468
 
469
+ void swap(Store &other) BOOST_NOEXCEPT_OR_NOTHROW {
470
+ std::swap(schema, other.schema);
471
+ entries.swap(other.entries);
472
+ std::swap(updatedOnce, other.updatedOnce);
473
+ }
474
+
309
475
  /**
310
476
  * Inspects the current store's configuration keys and values in a format
311
477
  * that displays user-supplied and effective values, as well as
@@ -320,17 +486,20 @@ public:
320
486
  const Entry &entry = it.getValue();
321
487
  Json::Value subdoc(Json::objectValue);
322
488
 
323
- subdoc["user_value"] = maybeFilterPassword(entry, entry.userValue);
324
- if (entry.schemaEntry->defaultValueGetter) {
325
- subdoc["default_value"] = maybeFilterPassword(entry, entry.getDefaultValue(*this));
326
- }
327
- subdoc["effective_value"] = maybeFilterPassword(entry, entry.getEffectiveValue(*this));
328
489
  entry.schemaEntry->inspect(subdoc);
490
+ subdoc["user_value"] = entry.userValue;
491
+ subdoc["effective_value"] = entry.getEffectiveValue(*this);
492
+ if (entry.schemaEntry->defaultValueGetter && entry.schemaEntry->flags & _DYNAMIC_DEFAULT_VALUE) {
493
+ subdoc["default_value"] = entry.getDefaultValue(*this);
494
+ }
329
495
 
330
496
  result[it.getKey()] = subdoc;
331
497
  it.next();
332
498
  }
333
499
 
500
+ applyInspectFilters(result);
501
+ doFilterSecrets(result);
502
+
334
503
  return result;
335
504
  }
336
505
 
@@ -339,6 +508,7 @@ public:
339
508
  * values only. This is like `inspect()` but much less verbose.
340
509
  * See the README's "Inspecting all data" section to learn more
341
510
  * about the format.
511
+ * Note that values with the SECRET flag are not filtered.
342
512
  */
343
513
  Json::Value inspectEffectiveValues() const {
344
514
  Json::Value result(Json::objectValue);
@@ -352,6 +522,24 @@ public:
352
522
 
353
523
  return result;
354
524
  }
525
+
526
+ /**
527
+ * Inspects the current store's configuration keys and user
528
+ * values only. This is like `inspect()` but much less verbose.
529
+ * Note that values with the SECRET flag are not filtered.
530
+ */
531
+ Json::Value inspectUserValues() const {
532
+ Json::Value result(Json::objectValue);
533
+ StringKeyTable<Entry>::ConstIterator it(entries);
534
+
535
+ while (*it != NULL) {
536
+ const Entry &entry = it.getValue();
537
+ result[it.getKey()] = entry.userValue;
538
+ it.next();
539
+ }
540
+
541
+ return result;
542
+ }
355
543
  };
356
544
 
357
545
 
@@ -390,6 +578,37 @@ Schema::validateSubSchema(const Store &store, vector<Error> &errors,
390
578
  }
391
579
  }
392
580
 
581
+ template<typename Translator>
582
+ inline Json::Value
583
+ Schema::normalizeSubSchema(const Json::Value &effectiveValues,
584
+ const Schema *mainSchema, const Schema *subschema,
585
+ const Translator *translator, const Normalizer &origNormalizer)
586
+ {
587
+ Json::Value translatedEffectiveValues(Json::objectValue);
588
+ StringKeyTable<Entry>::ConstIterator it(subschema->entries);
589
+
590
+ while (*it != NULL) {
591
+ const HashedStaticString &subSchemaKey = it.getKey();
592
+ const string mainSchemaKey = translator->reverseTranslateOne(
593
+ subSchemaKey);
594
+ const Entry *mainSchemaEntry;
595
+
596
+ if (mainSchema->entries.lookup(mainSchemaKey, &mainSchemaEntry)) {
597
+ translatedEffectiveValues[subSchemaKey] = effectiveValues[mainSchemaKey];
598
+ }
599
+
600
+ it.next();
601
+ }
602
+
603
+ return translator->reverseTranslate(origNormalizer(translatedEffectiveValues));
604
+ }
605
+
606
+ inline Json::Value
607
+ Schema::getStaticDefaultValue(const Schema::Entry &entry) {
608
+ Store::Entry storeEntry(entry);
609
+ return Store::maybeFilterSecret(storeEntry, storeEntry.getDefaultValue(Store()));
610
+ }
611
+
393
612
 
394
613
  } // namespace ConfigKit
395
614
  } // namespace Passenger