passenger 5.0.1 → 5.0.2

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 (42) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/.editorconfig +10 -0
  5. data/CHANGELOG +12 -0
  6. data/CONTRIBUTING.md +10 -0
  7. data/CONTRIBUTORS +1 -0
  8. data/Gemfile +10 -10
  9. data/build/basics.rb +5 -1
  10. data/build/common_library.rb +33 -55
  11. data/build/debian.rb +2 -4
  12. data/build/packaging.rb +3 -3
  13. data/build/preprocessor.rb +3 -204
  14. data/debian.template/{control.template → control.erb} +18 -18
  15. data/debian.template/{locations.ini.template → locations.ini.erb} +0 -0
  16. data/debian.template/{passenger-dev.install.template → passenger-dev.install.erb} +0 -0
  17. data/debian.template/passenger-doc.install.erb +2 -0
  18. data/debian.template/{passenger.install.template → passenger.install.erb} +0 -0
  19. data/debian.template/{rules.template → rules.erb} +24 -23
  20. data/doc/users_guide_snippets/tips.txt +9 -1
  21. data/ext/common/Constants.h +3 -1
  22. data/ext/common/ServerKit/Channel.h +22 -4
  23. data/ext/common/ServerKit/Context.h +3 -1
  24. data/ext/common/ServerKit/FdSinkChannel.h +9 -1
  25. data/ext/common/ServerKit/FdSourceChannel.h +9 -1
  26. data/ext/common/ServerKit/FileBufferedChannel.h +19 -7
  27. data/ext/common/Utils/SystemMetricsCollector.h +0 -1
  28. data/ext/common/Utils/VariantMap.h +22 -1
  29. data/ext/common/agents/HelperAgent/Main.cpp +15 -0
  30. data/ext/common/agents/HelperAgent/OptionParser.h +6 -1
  31. data/ext/common/agents/HelperAgent/RequestHandler.h +3 -0
  32. data/lib/phusion_passenger.rb +1 -1
  33. data/lib/phusion_passenger/common_library.rb +13 -5
  34. data/lib/phusion_passenger/config/restart_app_command.rb +8 -4
  35. data/lib/phusion_passenger/constants.rb +1 -0
  36. data/lib/phusion_passenger/standalone/command.rb +1 -1
  37. data/lib/phusion_passenger/standalone/start_command.rb +8 -0
  38. data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +1 -0
  39. data/test/cxx/ServerKit/FileBufferedChannelTest.cpp +179 -4
  40. metadata +8 -8
  41. metadata.gz.asc +7 -7
  42. data/debian.template/passenger-doc.install.template +0 -2
@@ -1,23 +1,23 @@
1
1
  Source: passenger
2
2
  Section: ruby
3
3
  Priority: optional
4
- Maintainer: John Leach <john@brightbox.co.uk>
4
+ Maintainer: Phusion <info@phusion.nl>
5
5
  Uploaders: Phusion <info@phusion.nl>, Hongli Lai <hongli@phusion.nl>
6
6
  Build-Depends: debhelper (>= 7.0.50~), rake,
7
- #if is_distribution?("<= saucy") || is_distribution?("<= wheezy")
8
- ruby1.8, ruby1.8-dev, rubygems1.8,
9
- #endif
7
+ <% if is_distribution?("<= lucid") -%>
8
+ ruby1.8, ruby1.8-dev, rubygems1.8,
9
+ ruby1.9.1, ruby1.9.1-dev, rubygems1.9,
10
+ <% elsif is_distribution?("<= saucy") || is_distribution?("<= wheezy") -%>
11
+ ruby1.8, ruby1.8-dev, rubygems1.8,
10
12
  ruby1.9.1, ruby1.9.1-dev,
11
- #if is_distribution?('<= lucid')
12
- rubygems1.9,
13
- #endif
14
- #if is_distribution?(">= trusty") || is_distribution?(">= jessie")
15
- ruby2.0, ruby2.0-dev,
16
- #endif
13
+ <% else -%>
14
+ ruby1.9.1, ruby1.9.1-dev,
15
+ ruby2.0, ruby2.0-dev,
16
+ <% end -%>
17
17
  apache2-mpm-worker | apache2-mpm, apache2-threaded-dev,
18
- #if is_distribution?('>= precise') || is_distribution?('>= wheezy')
19
- libev-dev (>= 1:4.0.0),
20
- #endif
18
+ <% if is_distribution?('>= precise') || is_distribution?('>= wheezy') -%>
19
+ libev-dev (>= 1:4.0.0),
20
+ <% end -%>
21
21
  libapr1-dev, libcurl4-openssl-dev
22
22
  Standards-Version: 3.9.3
23
23
  Homepage: https://www.phusionpassenger.com/
@@ -30,11 +30,11 @@ Architecture: any
30
30
  Depends: ${shlibs:Depends}, ${misc:Depends},
31
31
  ruby2.4 | ruby2.3 | ruby2.2 | ruby2.1 | ruby2.0 | ruby1.9.1 | ruby1.8 | ruby-interpreter,
32
32
  rubygems-integration (>= 1.8) | rubygems1.9.1 | rubygems1.9 | rubygems (>= 1.2),
33
- #if is_distribution?('<= lucid') || is_distribution?('<= squeeze')
34
- librack-ruby
35
- #else
36
- ruby-rack
37
- #endif
33
+ <% if is_distribution?('<= lucid') || is_distribution?('<= squeeze') -%>
34
+ librack-ruby
35
+ <% else -%>
36
+ ruby-rack
37
+ <% end -%>
38
38
  Recommends: passenger-doc (= ${binary:Version}), passenger-dev (= ${binary:Version})
39
39
  Breaks: libapache2-mod-passenger (<< <%= DEBIAN_EPOCH %>:<%= PACKAGE_VERSION %>), passenger-common,
40
40
  passenger-common1.8, passenger-common1.9.1, ruby-passenger
@@ -0,0 +1,2 @@
1
+ debian/tmp/usr/share/doc/<%= PhusionPassenger::GLOBAL_NAMESPACE_DIRNAME %>/* usr/share/doc/<%= PhusionPassenger::GLOBAL_NAMESPACE_DIRNAME %>/
2
+ CONTRIBUTORS usr/share/doc/<%= PhusionPassenger::GLOBAL_NAMESPACE_DIRNAME %>/
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/make -f
2
2
  # export DH_VERBOSE=1
3
3
 
4
- #if is_distribution?('>= precise') || is_distribution?('>= wheezy')
5
- export USE_VENDORED_LIBEV=false
6
- #endif
4
+ <% if is_distribution?('>= precise') || is_distribution?('>= wheezy') -%>
5
+ export USE_VENDORED_LIBEV=false
6
+ <% end %>
7
7
 
8
- #if ['1', 'true', 'on', 'yes'].include?(ENV['USE_CCACHE'])
9
- export USE_CCACHE=1
10
- NGINX_CONFIGURE_OPTIONS = CC=/usr/lib/ccache/cc CXX=/usr/lib/ccache/c++
11
- #else
12
- NGINX_CONFIGURE_OPTIONS =
13
- #endif
8
+ <% if ['1', 'true', 'on', 'yes'].include?(ENV['USE_CCACHE']) -%>
9
+ export USE_CCACHE=1
10
+ NGINX_CONFIGURE_OPTIONS = CC=/usr/lib/ccache/cc CXX=/usr/lib/ccache/c++
11
+ <% else -%>
12
+ NGINX_CONFIGURE_OPTIONS =
13
+ <% end -%>
14
14
 
15
15
  # Speed up ccache (reduce I/O) by lightly compressing things.
16
16
  # Always set these variables because pbuilder uses ccache transparently.
@@ -26,16 +26,17 @@ override_dh_auto_configure:
26
26
  # Do nothing
27
27
 
28
28
  override_dh_auto_build:
29
- #if is_distribution?("<= saucy") || is_distribution?("<= wheezy")
30
- /usr/bin/ruby1.8 /usr/bin/rake fakeroot
31
- mv pkg/fakeroot pkg/fakeroot1.8
32
- #endif
29
+ <% if is_distribution?("<= saucy") || is_distribution?("<= wheezy") -%>
30
+ /usr/bin/ruby1.8 /usr/bin/rake fakeroot
31
+ mv pkg/fakeroot pkg/fakeroot1.8
33
32
  /usr/bin/ruby1.9.1 /usr/bin/rake fakeroot
34
33
  mv pkg/fakeroot pkg/fakeroot1.9.1
35
- #if is_distribution?(">= trusty") || is_distribution?(">= jessie")
36
- /usr/bin/ruby2.0 /usr/bin/rake fakeroot
37
- mv pkg/fakeroot pkg/fakeroot2.0
38
- #endif
34
+ <% else -%>
35
+ /usr/bin/ruby1.9.1 /usr/bin/rake fakeroot
36
+ mv pkg/fakeroot pkg/fakeroot1.9.1
37
+ /usr/bin/ruby2.0 /usr/bin/rake fakeroot
38
+ mv pkg/fakeroot pkg/fakeroot2.0
39
+ <% end -%>
39
40
  cd nginx-<%= PhusionPassenger::PREFERRED_NGINX_VERSION %> && \
40
41
  env $(NGINX_CONFIGURE_OPTIONS) ./configure --prefix=/tmp \
41
42
  <%= PhusionPassenger::STANDALONE_NGINX_CONFIGURE_OPTIONS %> \
@@ -46,13 +47,13 @@ override_dh_auto_build:
46
47
  override_dh_auto_install:
47
48
  mkdir debian/tmp/
48
49
  # Merge Ruby 1.8, 1.9 and 2.0 files into a single directory.
49
- #if is_distribution?("<= saucy") || is_distribution?("<= wheezy")
50
- cp -a pkg/fakeroot1.8/* debian/tmp/
51
- #endif
50
+ <% if is_distribution?("<= saucy") || is_distribution?("<= wheezy") -%>
51
+ cp -a pkg/fakeroot1.8/* debian/tmp/
52
+ cp -a pkg/fakeroot1.9.1/* debian/tmp/
53
+ <% else -%>
52
54
  cp -a pkg/fakeroot1.9.1/* debian/tmp/
53
- #if is_distribution?(">= trusty") || is_distribution?(">= jessie")
54
- cp -a pkg/fakeroot2.0/* debian/tmp/
55
- #endif
55
+ cp -a pkg/fakeroot2.0/* debian/tmp/
56
+ <% end -%>
56
57
  ./dev/install_scripts_bootstrap_code.rb --ruby /usr/lib/ruby/vendor_ruby debian/tmp/usr/bin/* debian/tmp/usr/sbin/*
57
58
  ./dev/install_scripts_bootstrap_code.rb --nginx-module-config /usr/bin debian/tmp/usr/share/<%= PhusionPassenger::GLOBAL_NAMESPACE_DIRNAME %>/ngx_http_passenger_module/config
58
59
  touch debian/tmp/usr/share/<%= PhusionPassenger::GLOBAL_NAMESPACE_DIRNAME %>/release.txt
@@ -515,7 +515,6 @@ Extra environment variables: `PASSENGER_PROCESS_PID`, `PASSENGER_APP_ROOT`. Erro
515
515
  `queue_full_error` (since 5.0.0 RC 1)::
516
516
  The server rejects new requests (default: HTTP 503) while the request queue is full (https://www.phusionpassenger.com/documentation/Users%20guide%20Nginx.html#passenger_max_request_queue_size).
517
517
  This hook gets called for each rejection.
518
-
519
518
  +
520
519
  Extra environment variables:
521
520
  +
@@ -537,6 +536,15 @@ Note that the `after_initialize_supergroup` hook may be called while this hook i
537
536
  +
538
537
  Extra environment variables: `PASSENGER_APP_ROOT`.
539
538
 
539
+ `max_request_time_reached` (since 5.0.2, Enterprise-only)::
540
+ Called when a <<PassengerMaxRequestTime,max request time limit>> has been reached. Please note that as soon as this hook has finished executing, the application process will be killed with SIGKILL. So if you want to perform any diagnostics on the process in question (e.g. with strace, gdb, etc), please do not exit your hook script until you've obtained all the diagnostics you want.
541
+ +
542
+ Extra environment variables:
543
+ +
544
+ - `PASSENGER_APP_ID`: the PID of the process whose request took too long.
545
+ - `PASSENGER_REQUEST_PATH`: the path of the request that took took long, e.g. "/photos/edit?id=123".
546
+ - `PASSENGER_REQUEST_HOST`: the host name of the request that took too long, e.g. "www.example.com". This environment variable is not set if the request doesn't contain a Host header.
547
+
540
548
  [[flying_passenger]]
541
549
  === Flying Passenger
542
550
  ifdef::apache[]
@@ -60,6 +60,8 @@
60
60
 
61
61
  #define DEFAULT_CONCURRENCY_MODEL "process"
62
62
 
63
+ #define DEFAULT_FILE_BUFFERED_CHANNEL_THRESHOLD 131072
64
+
63
65
  #define DEFAULT_HTTP_SERVER_LISTEN_ADDRESS "tcp://127.0.0.1:3000"
64
66
 
65
67
  #define DEFAULT_LOGGING_AGENT_ADMIN_LISTEN_ADDRESS "tcp://127.0.0.1:9345"
@@ -112,7 +114,7 @@
112
114
 
113
115
  #define NGINX_DOC_URL "https://www.phusionpassenger.com/documentation/Users%20guide%20Nginx.html"
114
116
 
115
- #define PASSENGER_VERSION "5.0.1"
117
+ #define PASSENGER_VERSION "5.0.2"
116
118
 
117
119
  #define POOL_HELPER_THREAD_STACK_SIZE 262144
118
120
 
@@ -36,6 +36,7 @@
36
36
  #include <MemoryKit/mbuf.h>
37
37
  #include <Logging.h>
38
38
  #include <Utils/StrIntUtils.h>
39
+ #include <Utils/json.h>
39
40
 
40
41
  namespace Passenger {
41
42
  namespace ServerKit {
@@ -61,10 +62,10 @@ using namespace boost;
61
62
  *
62
63
  * The data callback can consume the buffer immediately, and tell Channel how many bytes
63
64
  * it has consumed, and whether it accepts any further data, by returning a Channel::Result.
64
- * If the buffer was not fully consumed the data callback, and it is still willing to accept
65
- * further data (by not transitioning to the end state or an error state), then Channel will
66
- * call the data callback again with the remainder of the buffer. This repeats
67
- * until:
65
+ * If the buffer was not fully consumed by the data callback, and the callback is still
66
+ * willing to accept further data (by not transitioning to the end state or an error state),
67
+ * then Channel will call the data callback again with the remainder of the buffer. This
68
+ * repeats until:
68
69
  *
69
70
  * * the buffer is fully consumed,
70
71
  * * or until the callback indicates that it's no longer accepting further data,
@@ -149,6 +150,8 @@ using namespace boost;
149
150
  * when the Channel is not accepting reads, and it starts reading from the file
150
151
  * descriptor when the channel is accepting reads again.
151
152
  *
153
+ * ## Asynchronous consumption
154
+ *
152
155
  * The data callback can also tell Channel that it wants to consume the buffer
153
156
  * *asynchronously*, by returning a Channel::Result with a negative consumption size.
154
157
  * At some later point, something must notify Channel that the buffer is consumed,
@@ -738,6 +741,21 @@ public:
738
741
  bool endAcked() const {
739
742
  return state == EOF_REACHED;
740
743
  }
744
+
745
+ Json::Value inspectAsJson() const {
746
+ Json::Value doc;
747
+
748
+ doc["callback_in_progress"] = !acceptingInput();
749
+ if (hasError()) {
750
+ doc["error"] = errcode;
751
+ doc["error_acked"] = endAcked();
752
+ } else if (ended()) {
753
+ doc["ended"] = true;
754
+ doc["end_acked"] = endAcked();
755
+ }
756
+
757
+ return doc;
758
+ }
741
759
  };
742
760
 
743
761
 
@@ -43,13 +43,15 @@ struct FileBufferedChannelConfig {
43
43
  string bufferDir;
44
44
  unsigned int threshold;
45
45
  unsigned int delayInFileModeSwitching;
46
+ unsigned int maxDiskChunkReadSize;
46
47
  bool autoTruncateFile;
47
48
  bool autoStartMover;
48
49
 
49
50
  FileBufferedChannelConfig()
50
51
  : bufferDir("/tmp"),
51
- threshold(1024 * 128),
52
+ threshold(DEFAULT_FILE_BUFFERED_CHANNEL_THRESHOLD),
52
53
  delayInFileModeSwitching(0),
54
+ maxDiskChunkReadSize(0),
53
55
  autoTruncateFile(true),
54
56
  autoStartMover(true)
55
57
  { }
@@ -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
  *
@@ -30,6 +30,7 @@
30
30
  #include <unistd.h>
31
31
  #include <ev.h>
32
32
  #include <ServerKit/Channel.h>
33
+ #include <Utils/json.h>
33
34
 
34
35
  namespace Passenger {
35
36
  namespace ServerKit {
@@ -197,6 +198,13 @@ public:
197
198
  void setHooks(Hooks *hooks) {
198
199
  this->hooks = hooks;
199
200
  }
201
+
202
+ Json::Value inspectAsJson() const {
203
+ Json::Value doc = Channel::inspectAsJson();
204
+ doc["initialized"] = watcher.fd != -1;
205
+ doc["io_watcher_active"] = (bool) watcher.active;
206
+ return doc;
207
+ }
200
208
  };
201
209
 
202
210
 
@@ -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 <MemoryKit/mbuf.h>
34
34
  #include <ServerKit/Context.h>
35
35
  #include <ServerKit/Channel.h>
36
+ #include <Utils/json.h>
36
37
 
37
38
  namespace Passenger {
38
39
  namespace ServerKit {
@@ -231,6 +232,13 @@ public:
231
232
  void setHooks(Hooks *hooks) {
232
233
  this->hooks = hooks;
233
234
  }
235
+
236
+ Json::Value inspectAsJson() const {
237
+ Json::Value doc = Channel::inspectAsJson();
238
+ doc["initialized"] = watcher.fd != -1;
239
+ doc["io_watcher_active"] = (bool) watcher.active;
240
+ return doc;
241
+ }
234
242
  };
235
243
 
236
244
 
@@ -389,7 +389,8 @@ private:
389
389
 
390
390
  /***** Buffer manipulation *****/
391
391
 
392
- void clearBuffers() {
392
+ void clearBuffers(bool mayCallCallbacks) {
393
+ unsigned int oldNbuffers = nbuffers;
393
394
  nbuffers = 0;
394
395
  bytesBuffered = 0;
395
396
  firstBuffer = MemoryKit::mbuf();
@@ -399,6 +400,9 @@ private:
399
400
  // a conditional here improves performance slightly.
400
401
  moreBuffers.clear();
401
402
  }
403
+ if (mayCallCallbacks && oldNbuffers != 0) {
404
+ callBuffersFlushedCallback();
405
+ }
402
406
  }
403
407
 
404
408
  void pushBuffer(const MemoryKit::mbuf &buffer) {
@@ -553,6 +557,11 @@ private:
553
557
  if (config->autoTruncateFile) {
554
558
  FBC_DEBUG("Reader: no more buffers. Transitioning to RS_INACTIVE, truncating file");
555
559
  switchToInMemoryMode();
560
+ if (generation != this->generation || mode >= ERROR) {
561
+ // Callback deinitialized this object, or callback
562
+ // called a method that encountered an error.
563
+ return;
564
+ }
556
565
  } else {
557
566
  FBC_DEBUG("Reader: no more buffers. Transitioning to RS_INACTIVE, "
558
567
  "not truncating file because config->autoTruncateFile is turned off");
@@ -647,7 +656,10 @@ private:
647
656
  assert(inFileMode->written > 0);
648
657
  size_t size = std::min<size_t>(inFileMode->written,
649
658
  mbuf_pool_data_size(&ctx->mbuf_pool));
650
- FBC_DEBUG("Reader: reading next chunk from file");
659
+ if (config->maxDiskChunkReadSize > 0 && size > config->maxDiskChunkReadSize) {
660
+ size = config->maxDiskChunkReadSize;
661
+ }
662
+ FBC_DEBUG("Reader: reading next chunk from file, " << size << " bytes");
651
663
  verifyInvariants();
652
664
  ReadContext *readContext = new ReadContext(this);
653
665
  readContext->buffer = MemoryKit::mbuf_get(&ctx->mbuf_pool);
@@ -736,7 +748,6 @@ private:
736
748
  P_ASSERT_EQ(readerState, RS_FEEDING);
737
749
  verifyInvariants();
738
750
  if (acceptingInput()) {
739
- readerState = RS_INACTIVE;
740
751
  readNext();
741
752
  } else if (mayAcceptInputLater()) {
742
753
  readNextWhenChannelIdle();
@@ -797,6 +808,8 @@ private:
797
808
  * a new one, instead of calling `ftruncate()` or something.
798
809
  * This way, any pending I/O operations in the background won't
799
810
  * affect correctness.
811
+ *
812
+ * This method may call callbacks.
800
813
  */
801
814
  void switchToInMemoryMode() {
802
815
  P_ASSERT_EQ(mode, IN_FILE_MODE);
@@ -804,9 +817,9 @@ private:
804
817
 
805
818
  FBC_DEBUG("Recreating file, switching to in-memory mode");
806
819
  cancelWriter();
807
- clearBuffers();
808
820
  mode = IN_MEMORY_MODE;
809
821
  inFileMode.reset();
822
+ clearBuffers(true);
810
823
  }
811
824
 
812
825
 
@@ -1400,7 +1413,7 @@ public:
1400
1413
  if (mode == IN_FILE_MODE) {
1401
1414
  cancelWriter();
1402
1415
  }
1403
- clearBuffers();
1416
+ clearBuffers(false);
1404
1417
  mode = IN_MEMORY_MODE;
1405
1418
  readerState = RS_INACTIVE;
1406
1419
  errcode = 0;
@@ -1517,7 +1530,7 @@ public:
1517
1530
  }
1518
1531
 
1519
1532
  Json::Value inspectAsJson() const {
1520
- Json::Value doc;
1533
+ Json::Value doc = Channel::inspectAsJson();
1521
1534
 
1522
1535
  switch (mode) {
1523
1536
  case IN_MEMORY_MODE:
@@ -1542,7 +1555,6 @@ public:
1542
1555
  doc["reader_state"] = getReaderStateString();
1543
1556
  doc["nbuffers"] = nbuffers;
1544
1557
  doc["bytes_buffered"] = byteSizeToJson(getBytesBuffered());
1545
- doc["callback_in_progress"] = !acceptingInput();
1546
1558
 
1547
1559
  return doc;
1548
1560
  }
@@ -55,7 +55,6 @@
55
55
  #endif
56
56
  #ifdef __FreeBSD__
57
57
  #include <sys/param.h>
58
- #include <sys/pcpu.h>
59
58
  #include <sys/sysctl.h>
60
59
  #include <sys/resource.h>
61
60
  #include <vm/vm_param.h>
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010-2014 Phusion
3
+ * Copyright (c) 2010-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -207,6 +207,13 @@ public:
207
207
  return *this;
208
208
  }
209
209
 
210
+ VariantMap &setDefaultUint(const string &name, unsigned int value) {
211
+ if (store.find(name) == store.end()) {
212
+ store[name] = toString(value);
213
+ }
214
+ return *this;
215
+ }
216
+
210
217
  VariantMap &setULL(const string &name, unsigned long long value) {
211
218
  set(name, toString(value));
212
219
  return *this;
@@ -325,6 +332,20 @@ public:
325
332
  return result;
326
333
  }
327
334
 
335
+ unsigned int getUint(const string &name, bool required = true, unsigned int defaultValue = 0) const {
336
+ unsigned int result = defaultValue;
337
+ const string *str;
338
+ if (lookup(name, required, &str)) {
339
+ long long val = stringToLL(*str);
340
+ if (val < 0) {
341
+ result = 0;
342
+ } else {
343
+ result = (unsigned int) val;
344
+ }
345
+ }
346
+ return result;
347
+ }
348
+
328
349
  unsigned long long getULL(const string &name, bool required = true,
329
350
  unsigned long long defaultValue = 0) const
330
351
  {