passenger 5.0.0.beta1 → 5.0.0.beta2

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 (38) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/CHANGELOG +33 -0
  5. data/CONTRIBUTORS +2 -0
  6. data/build/agents.rb +3 -0
  7. data/build/packaging.rb +1 -1
  8. data/doc/Packaging.txt.md +1 -1
  9. data/doc/users_guide_snippets/tips.txt +1 -1
  10. data/ext/apache2/Hooks.cpp +3 -12
  11. data/ext/common/ApplicationPool2/Implementation.cpp +1 -1
  12. data/ext/common/ApplicationPool2/Options.h +3 -0
  13. data/ext/common/ApplicationPool2/Process.h +9 -11
  14. data/ext/common/ApplicationPool2/Spawner.h +6 -2
  15. data/ext/common/Constants.h +1 -1
  16. data/ext/common/ServerKit/HeaderTable.h +28 -4
  17. data/ext/common/ServerKit/Implementation.cpp +5 -1
  18. data/ext/common/agents/HelperAgent/Main.cpp +17 -0
  19. data/ext/common/agents/HelperAgent/OptionParser.h +15 -0
  20. data/ext/common/agents/HelperAgent/RequestHandler.h +6 -0
  21. data/ext/common/agents/HelperAgent/RequestHandler/AppResponse.h +1 -0
  22. data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +36 -0
  23. data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +1 -0
  24. data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +5 -4
  25. data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +28 -2
  26. data/ext/common/agents/HelperAgent/ResponseCache.h +1 -1
  27. data/ext/ruby/extconf.rb +2 -0
  28. data/ext/ruby/passenger_native_support.c +40 -13
  29. data/lib/phusion_passenger.rb +1 -1
  30. data/lib/phusion_passenger/config/admin_command_command.rb +2 -2
  31. data/lib/phusion_passenger/config/main.rb +1 -1
  32. data/lib/phusion_passenger/config/system_metrics_command.rb +7 -2
  33. data/lib/phusion_passenger/platform_info/operating_system.rb +6 -6
  34. data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +11 -1
  35. data/node_lib/phusion_passenger/line_reader.js +3 -1
  36. data/test/cxx/ServerKit/HeaderTableTest.cpp +7 -1
  37. metadata +2 -2
  38. metadata.gz.asc +7 -7
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- N2MwNTYyYWNkZDMzZGIxNDM5NWNhMGJlOTA5Y2RjNjE0Y2RjZjdhNw==
4
+ Yzc0NjVmNTJlYTZmYWExZjBmZTEyZDFjODliMDg2YTBiZWI5ZmQ2MA==
5
5
  data.tar.gz: !binary |-
6
- MzNlOTI1MWZkNDIzMTg0NWVmZjg4YWNlMWJiODU0MmRiMjMzMjFiMA==
6
+ YzJmMGQxZmU1NmY5ODcxNTBiZWZlZThmMjhiYTE1NjVmNThiYjkxNw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OTQ4YjNmNzNmZTYzYWIzYTc4MTA0MTE4ODZhNDM2NzVkNGZhMTk1OTQ5NzQy
10
- ZDU4Mjc4ZTlhOTI4ODc2ZWUyZjQ3MTk0NDA4NjU1MjBkMjc3OTBhYTNmYzdl
11
- MzY1ZDczZDQ4ZTRjNmUzNDUwMTBkODFlMjBjNmQ1ZTEwZjIyMGQ=
9
+ ZmY1NmQwMzNkZmFhYWQ1MWVjMDFkYWQwYzRlNDdhNzI1MjI3MjUwODUyOGI5
10
+ ZjFkMmRkOTY0NzE2YTUyYmI4YzEwNjJiOTIwZWNmNzMwMTc2YzM0NWEzMzY4
11
+ NGVhMjQ4MjA4MWRmMjkzMmMwOGE2MjdmNzVmY2M1ZWU2NmIxMTE=
12
12
  data.tar.gz: !binary |-
13
- YjQ5M2NlNDEzNTIzMTNiNjllZTQzZTczYzU1NjBhOTI0MDk3M2QzMWNlYjdl
14
- OTE5MGZkY2ZmZmMzNjAwN2UwYjM5NmJhYmRmZGQyNzQ1ODNkYzNkYWNjZTUy
15
- NzBlMGE5MDM2ZjZkOGM2ZGM1NWYzNTI5ZjRmNDM0ODdmYzMzMDQ=
13
+ MTA0ZGVkNjg2NWM1YzRkZTBmMjAwMDM0MmYyNjJjNTNiMmNiZWEzNTIyNTQw
14
+ ZDM0NzljODkwNWVlMjU5NDIxOWRkODMwNmJkNDc4MTE1ZmZmNjAxZTE3NmFj
15
+ YzE3ODgyOTM4N2EzYjg5NDJlNGRhMWQ3NjFmZTg2YWJjZmNlYTY=
@@ -2,11 +2,11 @@
2
2
  Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
3
3
  Comment: GPGTools - http://gpgtools.org
4
4
 
5
- iQEcBAABAgAGBQJUdIftAAoJECrHRaUKISqMuuYH/1upwgXMC5FAFeeO1+4LRGYr
6
- ISrBfAo406NssNSJXCZWXOS1Yquuv2r+8ZtEhmikXhueoCdKgEfkJs83jfxTBywG
7
- qRTCQhduyZlcmc0P2T7ZQBj2lHIqoeMajT8oiERm/xpLKn8snpujdrMDxxZTWqh5
8
- lFtj0srdw2/+1cKzpLi+3b21bnLUhZdWOylr52oNIEhtlYFJFl/xVK1Jim4KYtj8
9
- sGrN+dTCqqNIPJhMKyT7QAI4OMQ3sTV+N9w0Px2BKbpUk3VvPU5QtYeGVX5DMljW
10
- naCtmstWTa304LkvtkYT4XGAX/OkKoozl1swes05ePhiBYWIfhByuyHSd+HRPX8=
11
- =VpUk
5
+ iQEcBAABAgAGBQJUlA2PAAoJECrHRaUKISqMfFcH/RERmONzrpkI9JVi+JxjUFRf
6
+ u9QT0kBu8VDYhaZEHQHvhVsTKKu+6+9qa0td1B9CFf2M+pdEakCIqIWCxb0dQVme
7
+ /F4z+pprnCEUwc348iynv0evSQ7dUhQphNGL0cCXDGDuMrGxbQxP7DRiyc2h99un
8
+ CVWuQGwZvnMGnJ4gmQjTKLalnMVHxEkEwukuhPFbuMEt6lzo4JXzTc3/STRSkt1J
9
+ ZnXeFOR8ifkynyvNv7wgbIv1r4iAWSn0L2c1P2ou1MnghQhjBDs2XJDnZ1KjDuML
10
+ f1AoWn/VyWi4HkyqAvVhFxCCr0WoyWmQ2uhD5vikwPHVrxoKbQGP2OJwk+FbltI=
11
+ =CGbg
12
12
  -----END PGP SIGNATURE-----
data.tar.gz.asc CHANGED
@@ -2,11 +2,11 @@
2
2
  Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
3
3
  Comment: GPGTools - http://gpgtools.org
4
4
 
5
- iQEcBAABAgAGBQJUdIftAAoJECrHRaUKISqMFw8H/R0bjqRGi/R6YMr7ZIKs6kSM
6
- kWBA+k0OR7AAeKNlvWGJW1YlFRgn8dGQCqQOFKbOtXZUbxYOpuNJCnJPN9UQe60A
7
- XrN7GRa7JLBL9DAfz1/PR5KicQvZ/XZ//zegPXFsFdRE0QSbqGKZRdNIwL9TtjwV
8
- OVFJ0UWeUCWme281T27q8yuGPGNRh7C5ypB9nYy3kmVuCPrfddA0n+R2WD+6CVMe
9
- sLm/axbTbhXdJ8Fn/riuDPWNXvgayXCB+9Ds6bdblsRGwkrGqf392upyIv0stXp5
10
- rRytKymkXMAlg57MRwvGBIikxyV0B0AHjhtuqQ69TYXnt4rhNTn9xbI6JK5uQ2E=
11
- =R+7h
5
+ iQEcBAABAgAGBQJUlA2PAAoJECrHRaUKISqMKGAH/At3Qmxna8prw6C4gGN2quRk
6
+ B29MiVrX/mt82dUhM4+ENRSN4Anxzj+ZiIzjts/ko4LO5z7imX6/Yg9yVu+JX2+K
7
+ DPmmmvvAfFhwslVf3bbL5f9bmaS1AV9Vh0a0TJtN2kv+wKmOJ0olbA8mYmC3TQh+
8
+ Ypg23/0FgGNESTM19RL5cr4govT8xd2/qYAn+WauXqmxaXLfRZFLXpDOrVQJztIu
9
+ AZA7FfhEaFRUv+2YB2iiO474AUIdCNpksWIs/q3bKJHy+buMsytFCWD5UcBt5Cfu
10
+ rli1TCrwxgnHTes5rrXWvAeQyJzmwQmhA6A8co6F6xOArfZmxZRfegD8Z82fY/M=
11
+ =MeVu
12
12
  -----END PGP SIGNATURE-----
data/CHANGELOG CHANGED
@@ -1,3 +1,16 @@
1
+ Release 5.0.0 beta 2
2
+ --------------------
3
+
4
+ * Fixed handling of multiple Set-Cookie headers. Closes GH-1296.
5
+ * `passenger-config system-metrics` now works properly if the agent is installed in ~/.passenger. Closes GH-1304.
6
+ * Documentation enhancements by Igor Vuk. Closes GH-1318.
7
+ * Fixed some crasher bugs.
8
+ * [Standalone] User switching is now correctly disabled.
9
+ * [Standalone] Fixed the `--thread-count` parameter.
10
+ * [Apache] IPs set by mod_remoteip are now respected. Closes GH-1284.
11
+ * [Apache] Fixed support for gzipped chunked responses. Closes GH-1309.
12
+
13
+
1
14
  Release 5.0.0 beta 1
2
15
  --------------------
3
16
 
@@ -31,6 +44,26 @@ Minor changes:
31
44
  * [Standalone] `passenger-standalone.json`/`Passengerfile.json` no longer overrides command line options. Instead, command line options now have the highest priority.
32
45
 
33
46
 
47
+ Release 4.0.56
48
+ --------------
49
+
50
+ * Fixed a file descriptor leak that manifests when an error page is shown. Contributed by Paul Bonaud, closes GH-1325.
51
+ * Improved Node.js request load balancing. Closes GH-1322. Thanks to Charles Vallières for the analysis.
52
+
53
+
54
+ Release 4.0.55
55
+ --------------
56
+
57
+ * Supports Ruby 2.2. Closes GH-1314.
58
+ * Fixed Linux OS name detection.
59
+
60
+
61
+ Release 4.0.54
62
+ --------------
63
+
64
+ * Contains a licensing-related hot fix for Enterprise customers.
65
+
66
+
34
67
  Release 4.0.53
35
68
  --------------
36
69
 
@@ -30,6 +30,7 @@ Gokulnath Manakkattil
30
30
  Gregory Potamianos
31
31
  Hongli Lai (Phusion)
32
32
  Ian Ehlert
33
+ Igor Vuk
33
34
  isaac
34
35
  Isaac Reuben
35
36
  J Smith
@@ -57,6 +58,7 @@ Nathaniel Bibler
57
58
  Neil Wilson
58
59
  Ninh Bui (Phusion)
59
60
  Pat Downey
61
+ Paul B
60
62
  Paul Kmiec
61
63
  Pepijn Looije
62
64
  Perry Smith
@@ -62,6 +62,9 @@ AGENT_OBJECTS = {
62
62
  'ext/common/ServerKit/HttpHeaderParser.h',
63
63
  'ext/common/ServerKit/AcceptLoadBalancer.h',
64
64
  'ext/common/ServerKit/FileBufferedChannel.h',
65
+ 'ext/common/ApplicationPool2/Pool.h',
66
+ 'ext/common/ApplicationPool2/Group.h',
67
+ 'ext/common/ApplicationPool2/Spawner.h',
65
68
  'ext/common/Constants.h',
66
69
  'ext/common/StaticString.h',
67
70
  'ext/common/Account.h',
@@ -142,7 +142,7 @@ task 'package:release' => ['package:set_official', 'package:gem', 'package:tarba
142
142
  end
143
143
 
144
144
  if is_open_source?
145
- if boolean_option('HOMEBREW_UPDATE', true)
145
+ if boolean_option('HOMEBREW_UPDATE', true) && !is_beta
146
146
  puts "Updating Homebrew formula..."
147
147
  Rake::Task['package:update_homebrew'].invoke
148
148
  else
@@ -233,7 +233,7 @@ a list of all possible assets and asset directories.
233
233
 
234
234
  The directory that contains the source code for the Phusion Passenger Ruby
235
235
  extension. Phusion Passenger uses these sources to build a Ruby extension,
236
- when it detects that the user is using a new Ruby interpeter for which
236
+ when it detects that the user is using a new Ruby interpreter for which
237
237
  no Ruby extension has been compiled.
238
238
 
239
239
  Value when originally packaged: `<SOURCE_ROOT>/ext/ruby`.
@@ -799,7 +799,7 @@ server {
799
799
  --------------------
800
800
  endif::nginx[]
801
801
 
802
- Then you need to install a Ruby 1.9-compatible Ruby interpeter with POSIX spawn support, alongside JRuby/MRI 1.8. Ruby interpreters which can be used for running the Flying Passenger daemon include:
802
+ Then you need to install a Ruby 1.9-compatible Ruby interpreter with POSIX spawn support, alongside JRuby/MRI 1.8. Ruby interpreters which can be used for running the Flying Passenger daemon include:
803
803
 
804
804
  * MRI Ruby >= 1.9.
805
805
  * Rubinius.
@@ -706,16 +706,6 @@ private:
706
706
  return 0;
707
707
  }
708
708
 
709
- /**
710
- * Checks case-insensitively whether the given header is "Transfer-Encoding".
711
- */
712
- bool headerIsTransferEncoding(const char *headerName, size_t len) const {
713
- return len == sizeof("transfer-encoding") - 1 &&
714
- apr_tolower(headerName[0]) == (u_char) 't' &&
715
- apr_tolower(headerName[sizeof("transfer-encoding") - 2]) == (u_char) 'g' &&
716
- apr_strnatcasecmp(headerName + 1, "ransfer-encoding") == 0;
717
- }
718
-
719
709
  /**
720
710
  * Convert an HTTP header name to a CGI environment name.
721
711
  */
@@ -892,7 +882,7 @@ private:
892
882
 
893
883
  #if HTTP_VERSION(AP_SERVER_MAJORVERSION_NUMBER, AP_SERVER_MINORVERSION_NUMBER) >= 2004
894
884
  addHeader(result, P_STATIC_STRING("!~REMOTE_ADDR"),
895
- r->connection->client_ip);
885
+ r->useragent_ip);
896
886
  addHeader(r, result, P_STATIC_STRING("!~REMOTE_PORT"),
897
887
  r->connection->client_addr->port);
898
888
  #else
@@ -979,10 +969,11 @@ private:
979
969
 
980
970
  // Add flags.
981
971
  // C = Strip 100 Continue header
972
+ // D = Dechunk
982
973
  // B = Buffer request body
983
974
  // S = SSL
984
975
 
985
- result.append("!~FLAGS: C", sizeof("!~FLAGS: C") - 1);
976
+ result.append("!~FLAGS: CD", sizeof("!~FLAGS: CD") - 1);
986
977
  if (config->bufferUpload != DirConfig::DISABLED) {
987
978
  result.append("B", 1);
988
979
  }
@@ -184,7 +184,6 @@ void processAndLogNewSpawnException(SpawnException &e, const Options &options,
184
184
 
185
185
  try {
186
186
  int fd = -1;
187
- FdGuard guard(fd, true);
188
187
  string errorPage;
189
188
 
190
189
  UPDATE_TRACE_POINT();
@@ -199,6 +198,7 @@ void processAndLogNewSpawnException(SpawnException &e, const Options &options,
199
198
  getSystemTempDir());
200
199
  fd = mkstemp(filename);
201
200
  #endif
201
+ FdGuard guard(fd, true);
202
202
  if (fd == -1) {
203
203
  int e = errno;
204
204
  throw SystemException("Cannot generate a temporary filename",
@@ -283,6 +283,8 @@ public:
283
283
  */
284
284
  bool loadShellEnvvars;
285
285
 
286
+ bool userSwitching;
287
+
286
288
  /** Whether Union Station logging should be enabled. Enabling this option will
287
289
  * result in:
288
290
  *
@@ -455,6 +457,7 @@ public:
455
457
  rights(DEFAULT_BACKEND_ACCOUNT_RIGHTS),
456
458
  debugger(false),
457
459
  loadShellEnvvars(true),
460
+ userSwitching(true),
458
461
  analytics(false),
459
462
  raiseInternalError(false),
460
463
 
@@ -534,19 +534,17 @@ public:
534
534
  int busyness() const {
535
535
  /* Different processes within a Group may have different
536
536
  * 'concurrency' values. We want:
537
- * - Group.pqueue to sort the processes from least used to most used.
538
- * - to give processes with concurrency == 0 more priority over processes
539
- * with concurrency > 0.
540
- * Therefore, we describe our busyness as a percentage of 'concurrency', with
541
- * the percentage value in [0..INT_MAX] instead of [0..1].
537
+ * - the process with the smallest busyness to be be picked for routing.
538
+ * - to give processes with concurrency == 0 more priority (in general)
539
+ * over processes with concurrency > 0.
540
+ * Therefore, in case of processes with concurrency > 0, we describe our
541
+ * busyness as a percentage of 'concurrency', with the percentage value
542
+ * in [0..INT_MAX] instead of [0..1]. That way, the busyness value
543
+ * of processes with concurrency > 0 is usually higher than that of processes
544
+ * with concurrency == 0.
542
545
  */
543
546
  if (concurrency == 0) {
544
- // Allows Group.pqueue to give idle sockets more priority.
545
- if (sessions == 0) {
546
- return 0;
547
- } else {
548
- return 1;
549
- }
547
+ return sessions;
550
548
  } else {
551
549
  return (int) (((long long) sessions * INT_MAX) / (double) concurrency);
552
550
  }
@@ -924,7 +924,9 @@ protected:
924
924
 
925
925
  UPDATE_TRACE_POINT();
926
926
  userInfo = (struct passwd *) NULL;
927
- if (!options.user.empty()) {
927
+ if (!options.userSwitching) {
928
+ // Keep userInfo at NULL so that it's set to defaultUser's UID.
929
+ } else if (!options.user.empty()) {
928
930
  ret = getpwnam_r(options.user.c_str(), &pwd, pwdBuf.get(),
929
931
  pwdBufSize, &userInfo);
930
932
  if (ret != 0) {
@@ -953,7 +955,9 @@ protected:
953
955
  }
954
956
 
955
957
  UPDATE_TRACE_POINT();
956
- if (!options.group.empty()) {
958
+ if (!options.userSwitching) {
959
+ // Keep groupId at -1 so that it's set to defaultGroup's GID.
960
+ } else if (!options.group.empty()) {
957
961
  struct group *groupInfo = (struct group *) NULL;
958
962
 
959
963
  if (options.group == "!STARTUP_FILE!") {
@@ -110,7 +110,7 @@
110
110
 
111
111
  #define NGINX_DOC_URL "https://www.phusionpassenger.com/documentation/Users%20guide%20Nginx.html"
112
112
 
113
- #define PASSENGER_VERSION "5.0.0.beta1"
113
+ #define PASSENGER_VERSION "5.0.0.beta2"
114
114
 
115
115
  #define POOL_HELPER_THREAD_STACK_SIZE 262144
116
116
 
@@ -39,6 +39,9 @@ namespace ServerKit {
39
39
  using namespace std;
40
40
 
41
41
 
42
+ extern const HashedStaticString HTTP_COOKIE;
43
+ extern const HashedStaticString HTTP_SET_COOKIE;
44
+
42
45
  struct Header {
43
46
  LString key;
44
47
  LString val;
@@ -104,6 +107,18 @@ private:
104
107
  return v;
105
108
  }
106
109
 
110
+ OXT_FORCE_INLINE
111
+ static bool isCookieHeader(const Header *header) {
112
+ return header->hash == HTTP_COOKIE.hash()
113
+ && psg_lstr_cmp(&header->key, HTTP_COOKIE);
114
+ }
115
+
116
+ OXT_FORCE_INLINE
117
+ static bool isSetCookieHeader(const Header *header) {
118
+ return header->hash == HTTP_SET_COOKIE.hash()
119
+ && psg_lstr_cmp(&header->key, HTTP_SET_COOKIE);
120
+ }
121
+
107
122
  void repopulate(unsigned int desiredSize) {
108
123
  assert((desiredSize & (desiredSize - 1)) == 0); // Must be a power of 2
109
124
  assert(m_population * 4 <= desiredSize * 3);
@@ -248,8 +263,13 @@ public:
248
263
  return;
249
264
  } else if (psg_lstr_cmp(&cell->header->key, &header->key)) {
250
265
  // Cell matches, so merge value into header.
251
- // TODO: we need to merge Set-Cookie headers differently
252
- psg_lstr_append(&cell->header->val, pool, ",", 1);
266
+ if (isCookieHeader(header)) {
267
+ psg_lstr_append(&cell->header->val, pool, ";", 1);
268
+ } else if (isSetCookieHeader(header)) {
269
+ psg_lstr_append(&cell->header->val, pool, "\n", 1);
270
+ } else {
271
+ psg_lstr_append(&cell->header->val, pool, ",", 1);
272
+ }
253
273
  LString::Part *part = header->val.start;
254
274
  header->val.start = NULL;
255
275
  while (part != NULL) {
@@ -301,8 +321,12 @@ public:
301
321
  if (PHT_CIRCULAR_OFFSET(ideal, cell) < PHT_CIRCULAR_OFFSET(ideal, neighbor)) {
302
322
  // Erase current cell and move neighbor into this position,
303
323
  // then make the now-empty neighbor the new cell to remove.
304
- psg_lstr_deinit(&cell->header->key);
305
- psg_lstr_deinit(&cell->header->val);
324
+ if (cell->header != NULL) {
325
+ // A previous iteration in this loop
326
+ // could have made cell->header NULL.
327
+ psg_lstr_deinit(&cell->header->key);
328
+ psg_lstr_deinit(&cell->header->val);
329
+ }
306
330
  *cell = *neighbor;
307
331
  cell = neighbor;
308
332
  neighbor->header = NULL;
@@ -28,7 +28,9 @@ namespace Passenger {
28
28
  namespace ServerKit {
29
29
 
30
30
 
31
- extern const HashedStaticString TRANSFER_ENCODING;
31
+ // Define 'extern' so that the compiler doesn't output warnings.
32
+ extern const HashedStaticString HTTP_COOKIE;
33
+ extern const HashedStaticString HTTP_SET_COOKIE;
32
34
  extern const char DEFAULT_INTERNAL_SERVER_ERROR_RESPONSE[];
33
35
  extern const unsigned int DEFAULT_INTERNAL_SERVER_ERROR_RESPONSE_SIZE;
34
36
 
@@ -41,6 +43,8 @@ const char DEFAULT_INTERNAL_SERVER_ERROR_RESPONSE[] =
41
43
  "Internal server error\n";
42
44
  const unsigned int DEFAULT_INTERNAL_SERVER_ERROR_RESPONSE_SIZE =
43
45
  sizeof(DEFAULT_INTERNAL_SERVER_ERROR_RESPONSE) - 1;
46
+ const HashedStaticString HTTP_COOKIE("cookie");
47
+ const HashedStaticString HTTP_SET_COOKIE("set-cookie");
44
48
 
45
49
 
46
50
  } // namespace ServerKit
@@ -939,12 +939,29 @@ preinitialize(VariantMap &options) {
939
939
  }
940
940
  }
941
941
 
942
+ static string
943
+ inferDefaultGroup(const string &defaultUser) {
944
+ struct passwd *userEntry = getpwnam(defaultUser.c_str());
945
+ if (userEntry == NULL) {
946
+ throw ConfigurationException(
947
+ string("The user that PassengerDefaultUser refers to, '") +
948
+ defaultUser + "', does not exist.");
949
+ }
950
+ return getGroupName(userEntry->pw_gid);
951
+ }
952
+
942
953
  static void
943
954
  setAgentsOptionsDefaults() {
944
955
  VariantMap &options = *agentsOptions;
945
956
  set<string> defaultAddress;
946
957
  defaultAddress.insert(DEFAULT_HTTP_SERVER_LISTEN_ADDRESS);
947
958
 
959
+ options.setDefaultBool("user_switching", true);
960
+ options.setDefault("default_user", DEFAULT_WEB_APP_USER);
961
+ if (!options.has("default_group")) {
962
+ options.set("default_group",
963
+ inferDefaultGroup(options.get("default_user")));
964
+ }
948
965
  options.setDefaultStrSet("server_addresses", defaultAddress);
949
966
  options.setDefaultBool("multi_app", false);
950
967
  options.setDefault("environment", DEFAULT_APP_ENV);
@@ -78,6 +78,12 @@ serverUsage() {
78
78
  printf(" the given admin account. LEVEL indicates the\n");
79
79
  printf(" privilege level (see below). PASSWORDFILE must\n");
80
80
  printf(" point to a file containing the password\n");
81
+ printf(" --no-user-switching Disables user switching support\n");
82
+ printf(" --default-user NAME Default user to start apps as, when user\n");
83
+ printf(" switching is enabled. Default: " DEFAULT_WEB_APP_USER "\n");
84
+ printf(" --default-group NAME Default group to start apps as, when user\n");
85
+ printf(" switching is disabled. Default: the default\n");
86
+ printf(" user's primary group\n");
81
87
  printf("\n");
82
88
  printf("Application serving options (optional):\n");
83
89
  printf(" -e, --environment NAME Default framework environment name to use.\n");
@@ -208,6 +214,15 @@ parseServerOption(int argc, const char *argv[], int &i, VariantMap &options) {
208
214
  authorizations.push_back(argv[i + 1]);
209
215
  options.setStrSet("server_authorizations", authorizations);
210
216
  i += 2;
217
+ } else if (p.isFlag(argv[i], '\0', "--no-user-switching")) {
218
+ options.setBool("user_switching", false);
219
+ i++;
220
+ } else if (p.isValueFlag(argc, i, argv[i], '\0', "--default-user")) {
221
+ options.set("default_user", argv[i + 1]);
222
+ i += 2;
223
+ } else if (p.isValueFlag(argc, i, argv[i], '\0', "--default-group")) {
224
+ options.set("default_group", argv[i + 1]);
225
+ i += 2;
211
226
  } else if (p.isValueFlag(argc, i, argv[i], '\0', "--max-pool-size")) {
212
227
  options.setInt("max_pool_size", atoi(argv[i + 1]));
213
228
  i += 2;
@@ -161,6 +161,12 @@ using namespace oxt;
161
161
  using namespace ApplicationPool2;
162
162
 
163
163
 
164
+ namespace ServerKit {
165
+ extern const HashedStaticString HTTP_COOKIE;
166
+ extern const HashedStaticString HTTP_SET_COOKIE;
167
+ }
168
+
169
+
164
170
  class RequestHandler: public ServerKit::HttpServer<RequestHandler, Client> {
165
171
  public:
166
172
  enum BenchmarkMode {
@@ -120,6 +120,7 @@ public:
120
120
  boost::uint64_t bodyAlreadyRead;
121
121
 
122
122
  LString *date;
123
+ LString *setCookie;
123
124
  LString *cacheControl;
124
125
  LString *expiresHeader;
125
126
  LString *lastModifiedHeader;
@@ -300,6 +300,22 @@ onAppResponseBegin(Client *client, Request *req) {
300
300
  // Localize hash table operations for better CPU caching.
301
301
  oobw = resp->secureHeaders.lookup(PASSENGER_REQUEST_OOB_WORK) != NULL;
302
302
  resp->date = resp->headers.lookup(HTTP_DATE);
303
+ resp->setCookie = resp->headers.lookup(ServerKit::HTTP_SET_COOKIE);
304
+ if (resp->setCookie != NULL) {
305
+ // Remove Set-Cookie from resp->headers without deallocating it.
306
+ LString *copy;
307
+
308
+ copy = (LString *) psg_palloc(req->pool, sizeof(LString));
309
+ *copy = *resp->setCookie;
310
+
311
+ resp->setCookie->start = NULL;
312
+ resp->setCookie->end = NULL;
313
+ resp->setCookie->size = 0;
314
+ psg_lstr_append(resp->setCookie, req->pool, "x", 1);
315
+ resp->headers.erase(ServerKit::HTTP_SET_COOKIE);
316
+
317
+ resp->setCookie = copy;
318
+ }
303
319
  resp->headers.erase(HTTP_CONNECTION);
304
320
  resp->headers.erase(HTTP_STATUS);
305
321
  if (resp->bodyType == AppResponse::RBT_CONTENT_LENGTH) {
@@ -574,6 +590,26 @@ constructHeaderBuffersForResponse(Request *req, struct iovec *buffers,
574
590
  PUSH_STATIC_BUFFER("\r\n");
575
591
  }
576
592
 
593
+ if (resp->setCookie != NULL) {
594
+ PUSH_STATIC_BUFFER("Set-Cookie: ");
595
+ part = resp->setCookie->start;
596
+ while (part != NULL) {
597
+ if (part->size == 1 && part->data[0] == '\n') {
598
+ // HeaderTable joins multiple Set-Cookie headers together using \n.
599
+ PUSH_STATIC_BUFFER("\r\nSet-Cookie: ");
600
+ } else {
601
+ if (buffers != NULL) {
602
+ buffers[i].iov_base = (void *) part->data;
603
+ buffers[i].iov_len = part->size;
604
+ }
605
+ INC_BUFFER_ITER(i);
606
+ dataSize += part->size;
607
+ }
608
+ part = part->next;
609
+ }
610
+ PUSH_STATIC_BUFFER("\r\n");
611
+ }
612
+
577
613
  nCacheableBuffers = i;
578
614
 
579
615
  if (resp->bodyType == AppResponse::RBT_CONTENT_LENGTH) {
@@ -137,6 +137,7 @@ void reinitializeAppResponse(Client *client, Request *req) {
137
137
  resp->aux.bodyInfo.contentLength = 0; // Sets the entire union to 0.
138
138
  resp->bodyAlreadyRead = 0;
139
139
  resp->date = NULL;
140
+ resp->setCookie = NULL;
140
141
  resp->cacheControl = NULL;
141
142
  resp->expiresHeader = NULL;
142
143
  resp->lastModifiedHeader = NULL;
@@ -206,11 +206,12 @@ fillPoolOptionsFromAgentsOptions(Options &options) {
206
206
  options.loggingAgentAddress = loggingAgentAddress;
207
207
  options.loggingAgentUsername = P_STATIC_STRING("logging");
208
208
  options.loggingAgentPassword = loggingAgentPassword;
209
- if (!this->defaultUser.empty()) {
210
- options.defaultUser = defaultUser;
209
+ options.userSwitching = agentsOptions->getBool("user_switching");
210
+ if (agentsOptions->has("default_user")) {
211
+ options.defaultUser = agentsOptions->get("default_user");
211
212
  }
212
- if (!this->defaultGroup.empty()) {
213
- options.defaultGroup = defaultGroup;
213
+ if (agentsOptions->has("default_group")) {
214
+ options.defaultGroup = agentsOptions->get("default_group");
214
215
  }
215
216
  options.minProcesses = agentsOptions->getInt("min_instances");
216
217
  options.spawnMethod = agentsOptions->get("spawn_method");
@@ -478,6 +478,7 @@ httpHeaderToScgiUpperCase(unsigned char *data, unsigned int size) {
478
478
  struct HttpHeaderConstructionCache {
479
479
  StaticString methodStr;
480
480
  const LString *remoteAddr;
481
+ const LString *setCookie;
481
482
  bool cached;
482
483
  };
483
484
 
@@ -568,6 +569,7 @@ constructHeaderBuffersForHttpProtocol(Request *req, struct iovec *buffers,
568
569
  if (!cache.cached) {
569
570
  cache.methodStr = http_method_str(req->method);
570
571
  cache.remoteAddr = req->secureHeaders.lookup(REMOTE_ADDR);
572
+ cache.setCookie = req->headers.lookup(ServerKit::HTTP_SET_COOKIE);
571
573
  cache.cached = true;
572
574
  }
573
575
 
@@ -593,9 +595,33 @@ constructHeaderBuffersForHttpProtocol(Request *req, struct iovec *buffers,
593
595
  PUSH_STATIC_BUFFER(" HTTP/1.1\r\nConnection: close\r\n");
594
596
  }
595
597
 
598
+ if (cache.setCookie != NULL) {
599
+ LString::Part *part;
600
+
601
+ PUSH_STATIC_BUFFER("Set-Cookie: ");
602
+ part = cache.setCookie->start;
603
+ while (part != NULL) {
604
+ if (part->size == 1 && part->data[0] == '\n') {
605
+ // HeaderTable joins multiple Set-Cookie headers together using \n.
606
+ PUSH_STATIC_BUFFER("\r\nSet-Cookie: ");
607
+ } else {
608
+ if (buffers != NULL) {
609
+ buffers[i].iov_base = (void *) part->data;
610
+ buffers[i].iov_len = part->size;
611
+ }
612
+ INC_BUFFER_ITER(i);
613
+ dataSize += part->size;
614
+ }
615
+ part = part->next;
616
+ }
617
+ PUSH_STATIC_BUFFER("\r\n");
618
+ }
619
+
596
620
  while (*it != NULL) {
597
- if (it->header->hash == HTTP_CONNECTION.hash()
598
- && psg_lstr_cmp(&it->header->key, P_STATIC_STRING("connection")))
621
+ if ((it->header->hash == HTTP_CONNECTION.hash()
622
+ || it->header->hash == ServerKit::HTTP_SET_COOKIE.hash())
623
+ && (psg_lstr_cmp(&it->header->key, P_STATIC_STRING("connection"))
624
+ || psg_lstr_cmp(&it->header->key, ServerKit::HTTP_SET_COOKIE)))
599
625
  {
600
626
  it.next();
601
627
  continue;
@@ -460,7 +460,7 @@ public:
460
460
 
461
461
  // @pre prepareRequest() returned true
462
462
  bool requestAllowsFetching(Request *req) const {
463
- return req->method == HTTP_GET
463
+ return (req->method == HTTP_GET || req->method == HTTP_HEAD)
464
464
  && req->cacheControl == NULL
465
465
  && !req->hasPragmaHeader;
466
466
  }
@@ -37,8 +37,10 @@ end
37
37
  have_header('alloca.h')
38
38
  have_header('ruby/version.h')
39
39
  have_header('ruby/io.h')
40
+ have_header('ruby/thread.h')
40
41
  have_var('ruby_version')
41
42
  have_func('rb_thread_io_blocking_region')
43
+ have_func('rb_thread_call_without_gvl')
42
44
 
43
45
  with_cflags($CFLAGS) do
44
46
  create_makefile('passenger_native_support')
@@ -36,6 +36,9 @@
36
36
  #ifdef HAVE_RUBY_VERSION_H
37
37
  #include "ruby/version.h"
38
38
  #endif
39
+ #ifdef HAVE_RUBY_THREAD_H
40
+ #include "ruby/thread.h"
41
+ #endif
39
42
  #include <sys/types.h>
40
43
  #include <sys/stat.h>
41
44
  #include <sys/ioctl.h>
@@ -199,11 +202,19 @@ update_group_written_info(IOVectorGroup *group, ssize_t bytes_written) {
199
202
  int iovcnt;
200
203
  } WritevWrapperData;
201
204
 
202
- static VALUE
203
- writev_wrapper(void *ptr) {
204
- WritevWrapperData *data = (WritevWrapperData *) ptr;
205
- return (VALUE) writev(data->filedes, data->iov, data->iovcnt);
206
- }
205
+ #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
206
+ static void *
207
+ writev_wrapper(void *ptr) {
208
+ WritevWrapperData *data = (WritevWrapperData *) ptr;
209
+ return (void *) writev(data->filedes, data->iov, data->iovcnt);
210
+ }
211
+ #else
212
+ static VALUE
213
+ writev_wrapper(void *ptr) {
214
+ WritevWrapperData *data = (WritevWrapperData *) ptr;
215
+ return (VALUE) writev(data->filedes, data->iov, data->iovcnt);
216
+ }
217
+ #endif
207
218
  #endif
208
219
 
209
220
  static VALUE
@@ -320,7 +331,10 @@ f_generic_writev(VALUE fd, VALUE *array_of_components, unsigned int count) {
320
331
  writev_wrapper_data.filedes = fd_num;
321
332
  writev_wrapper_data.iov = groups[i].io_vectors;
322
333
  writev_wrapper_data.iovcnt = groups[i].count;
323
- #ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION
334
+ #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
335
+ ret = (int) rb_thread_call_without_gvl(writev_wrapper,
336
+ &writev_wrapper_data, RUBY_UBF_IO, NULL);
337
+ #elif defined(HAVE_RB_THREAD_IO_BLOCKING_REGION)
324
338
  ret = (int) rb_thread_io_blocking_region(writev_wrapper,
325
339
  &writev_wrapper_data, fd_num);
326
340
  #else
@@ -726,13 +740,23 @@ fs_watcher_wait_fd(VALUE _fd) {
726
740
  }
727
741
 
728
742
  #ifndef TRAP_BEG
729
- static VALUE
730
- fs_watcher_read_byte_from_fd_wrapper(void *_arg) {
731
- FSWatcherReadByteData *data = (FSWatcherReadByteData *) _arg;
732
- data->ret = read(data->fd, &data->byte, 1);
733
- data->error = errno;
734
- return Qnil;
735
- }
743
+ #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
744
+ static void *
745
+ fs_watcher_read_byte_from_fd_wrapper(void *_arg) {
746
+ FSWatcherReadByteData *data = (FSWatcherReadByteData *) _arg;
747
+ data->ret = read(data->fd, &data->byte, 1);
748
+ data->error = errno;
749
+ return NULL;
750
+ }
751
+ #else
752
+ static VALUE
753
+ fs_watcher_read_byte_from_fd_wrapper(void *_arg) {
754
+ FSWatcherReadByteData *data = (FSWatcherReadByteData *) _arg;
755
+ data->ret = read(data->fd, &data->byte, 1);
756
+ data->error = errno;
757
+ return Qnil;
758
+ }
759
+ #endif
736
760
  #endif
737
761
 
738
762
  static VALUE
@@ -743,6 +767,9 @@ fs_watcher_read_byte_from_fd(VALUE _arg) {
743
767
  data->ret = read(data->fd, &data->byte, 1);
744
768
  TRAP_END;
745
769
  data->error = errno;
770
+ #elif defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
771
+ rb_thread_call_without_gvl2(fs_watcher_read_byte_from_fd_wrapper,
772
+ data, RUBY_UBF_IO, NULL);
746
773
  #elif defined(HAVE_RB_THREAD_IO_BLOCKING_REGION)
747
774
  rb_thread_io_blocking_region(fs_watcher_read_byte_from_fd_wrapper,
748
775
  data, data->fd);
@@ -30,7 +30,7 @@ module PhusionPassenger
30
30
 
31
31
  PACKAGE_NAME = 'passenger'
32
32
  # Run 'rake ext/common/Constants.h' after changing this number.
33
- VERSION_STRING = '5.0.0.beta1'
33
+ VERSION_STRING = '5.0.0.beta2'
34
34
 
35
35
  PREFERRED_NGINX_VERSION = '1.6.2'
36
36
  NGINX_SHA256_CHECKSUM = 'b5608c2959d3e7ad09b20fc8f9e5bd4bc87b3bc8ba5936a513c04ed8f1391a18'
@@ -81,11 +81,11 @@ private
81
81
  opts.on("-i", "--stdin", "Read HTTP request body data from stdin") do
82
82
  options[:data_source] = :stdin
83
83
  end
84
- opts.on("-f", "--data-file", String, "Read HTTP request body data from the given#{nl}" +
84
+ opts.on("-f", "--data-file PATH", String, "Read HTTP request body data from the given#{nl}" +
85
85
  "file") do |value|
86
86
  options[:data_source] = value
87
87
  end
88
- opts.on("-a", "--agent", String, "The name of the socket to send the command#{nl}" +
88
+ opts.on("-a", "--agent NAME", String, "The name of the socket to send the command#{nl}" +
89
89
  "to. This specifies which agent the request#{nl}" +
90
90
  "is sent to. Choices: watchdog,#{nl}" +
91
91
  "server_admin, logging_admin.#{nl}" +
@@ -101,7 +101,7 @@ module Config
101
101
  puts "#{PROGRAM_NAME} installation management:"
102
102
  puts " validate-install Validate this #{PROGRAM_NAME} installation"
103
103
  puts " build-native-support Ensure that the native_support library for the current"
104
- puts " Ruby interpeter is built"
104
+ puts " Ruby interpreter is built"
105
105
  puts " install-agent Install the #{PROGRAM_NAME} agent binary"
106
106
  puts " install-standalone-runtime"
107
107
  puts " Install the #{PROGRAM_NAME} Standalone"
@@ -29,8 +29,13 @@ module Config
29
29
 
30
30
  class SystemMetricsCommand < Command
31
31
  def run
32
- exec("#{PhusionPassenger.support_binaries_dir}/#{AGENT_EXE}",
33
- "system-metrics", *@argv)
32
+ agent_exe = PhusionPassenger.find_support_binary(AGENT_EXE)
33
+ if agent_exe
34
+ exec(agent_exe, "system-metrics", *@argv)
35
+ else
36
+ abort "This command requires the #{PROGRAM_NAME} agent to be installed. " +
37
+ "Please install it by running `passenger-config install-agent`."
38
+ end
34
39
  end
35
40
  end
36
41
 
@@ -34,14 +34,14 @@ module PlatformInfo
34
34
  def self.os_name
35
35
  if rb_config['target_os'] =~ /darwin/ && (sw_vers = find_command('sw_vers'))
36
36
  return "macosx"
37
- elsif rb_config['target_os'] == "linux-"
37
+ elsif rb_config['target_os'] =~ /^linux-/
38
38
  return "linux"
39
39
  else
40
40
  return rb_config['target_os']
41
41
  end
42
42
  end
43
43
  memoize :os_name
44
-
44
+
45
45
  # The current platform's shared library extension ('so' on most Unices).
46
46
  def self.library_extension
47
47
  if os_name == "macosx"
@@ -50,7 +50,7 @@ module PlatformInfo
50
50
  return "so"
51
51
  end
52
52
  end
53
-
53
+
54
54
  # Returns the `uname` command, or nil if `uname` cannot be found.
55
55
  # In addition to looking for `uname` in `PATH`, this method also looks
56
56
  # for `uname` in /bin and /usr/bin, just in case the user didn't
@@ -129,7 +129,7 @@ module PlatformInfo
129
129
  elsif arch == "amd64"
130
130
  arch = "x86_64"
131
131
  end
132
-
132
+
133
133
  if arch == "x86"
134
134
  # Most x86 operating systems nowadays are probably running on
135
135
  # a CPU that supports both x86 and x86_64, but we're not gonna
@@ -146,7 +146,7 @@ module PlatformInfo
146
146
  end
147
147
  end
148
148
  memoize :cpu_architectures, true
149
-
149
+
150
150
  # Returns whether the OS's main CPU architecture supports the
151
151
  # x86/x86_64 sfence instruction.
152
152
  def self.supports_sfence_instruction?
@@ -161,7 +161,7 @@ module PlatformInfo
161
161
  }))
162
162
  end
163
163
  memoize :supports_sfence_instruction?, true
164
-
164
+
165
165
  # Returns whether the OS's main CPU architecture supports the
166
166
  # x86/x86_64 lfence instruction.
167
167
  def self.supports_lfence_instruction?
@@ -21,6 +21,7 @@
21
21
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
22
  # THE SOFTWARE.
23
23
 
24
+ require 'etc'
24
25
  PhusionPassenger.require_passenger_lib 'constants'
25
26
  PhusionPassenger.require_passenger_lib 'standalone/control_utils'
26
27
  PhusionPassenger.require_passenger_lib 'utils/shellwords'
@@ -66,6 +67,7 @@ private
66
67
  command = "#{@agent_exe} watchdog";
67
68
  command << " --passenger-root #{Shellwords.escape PhusionPassenger.install_spec}"
68
69
  command << " --daemonize"
70
+ command << " --no-user-switching"
69
71
  command << " --no-delete-pid-file"
70
72
  command << " --cleanup-pidfile #{Shellwords.escape @working_dir}/temp_dir_toucher.pid"
71
73
  add_param(command, :user, "--user")
@@ -77,6 +79,14 @@ private
77
79
  @options[:ctls].each do |ctl|
78
80
  command << " --ctl #{Shellwords.escape ctl}"
79
81
  end
82
+ if @options[:user]
83
+ command << " --default-user #{Shellwords.ecape @options[:user]}"
84
+ else
85
+ user = Etc.getpwuid(Process.uid).name
86
+ group = Etc.getgrgid(Process.gid).name
87
+ command << " --default-user #{Shellwords.escape user}"
88
+ command << " --default-group #{Shellwords.escape group}"
89
+ end
80
90
 
81
91
  command << " --BS"
82
92
  command << " --listen #{listen_address}"
@@ -100,7 +110,7 @@ private
100
110
  add_param(command, :max_pool_size, "--max-pool-size")
101
111
  add_param(command, :min_instances, "--min-instances")
102
112
  add_enterprise_param(command, :concurrency_model, "--concurrency-model")
103
- add_enterprise_param(command, :thread_count, "--thread-count")
113
+ add_enterprise_param(command, :thread_count, "--app-thread-count")
104
114
  add_enterprise_flag_param(command, :rolling_restarts, "--rolling-restarts")
105
115
  add_enterprise_flag_param(command, :resist_deployment_errors, "--resist-deployment-errors")
106
116
  add_flag_param(command, :sticky_sessions, "--sticky-sessions")
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2012-2013 Phusion
3
+ * Copyright (c) 2012-2014 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -44,6 +44,8 @@ function LineReader(stream) {
44
44
  this.paused = false;
45
45
 
46
46
  function handleLineBuffer() {
47
+ var line, callback;
48
+
47
49
  if (self.lineBufferIsFull()) {
48
50
  stream.pause();
49
51
  self.paused = true;
@@ -163,9 +163,15 @@ namespace tut {
163
163
  table.insert(createHeader("X-Forwarded-For", "bar.com"), pool);
164
164
  table.insert(createHeader("Cache-Control", "must-invalidate"), pool);
165
165
  table.insert(createHeader("Cache-Control", "private"), pool);
166
+ table.insert(createHeader("cookie", "a"), pool);
167
+ table.insert(createHeader("cookie", "b"), pool);
168
+ table.insert(createHeader("set-cookie", "c=123"), pool);
169
+ table.insert(createHeader("set-cookie", "d=456"), pool);
166
170
 
167
- ensure_equals(table.size(), 2u);
171
+ ensure_equals(table.size(), 4u);
168
172
  ensure("(1)", psg_lstr_cmp(table.lookup("X-Forwarded-For"), "foo.com,bar.com"));
169
173
  ensure("(2)", psg_lstr_cmp(table.lookup("Cache-Control"), "must-invalidate,private"));
174
+ ensure("(3)", psg_lstr_cmp(table.lookup("cookie"), "a;b"));
175
+ ensure("(4)", psg_lstr_cmp(table.lookup("set-cookie"), "c=123\nd=456"));
170
176
  }
171
177
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passenger
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0.beta1
4
+ version: 5.0.0.beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phusion - http://www.phusion.nl/
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-25 00:00:00.000000000 Z
11
+ date: 2014-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
metadata.gz.asc CHANGED
@@ -2,11 +2,11 @@
2
2
  Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
3
3
  Comment: GPGTools - http://gpgtools.org
4
4
 
5
- iQEcBAABAgAGBQJUdIftAAoJECrHRaUKISqMv7MIAJsk0dOWHUL73jKLsffZM5G+
6
- gzWSbdWzJnOThR+E7PDxEfzjdeDvUm4w/BJKtJFQpzI+pvZpIaXyu55hzuM//Jf1
7
- ewICbRB+S28idU2d/jd1uYcKBUJv7iA4HNx4XvjaTRy0mu2Z9xpyiNv83pcd7VRp
8
- XDyfJFM5iYXs4Guiafxvo+2PUz7bJG35ex2fBsxQ5Uy04wzOgdzmCW1OkZHmHGLV
9
- ebx84EvphhLlxfNZ7C9Ga6W/hqiTvA+VF2/Zcc6miz9BWyf7EZZbq7/t7bm3u5A9
10
- Qw2pmjyNnWaKn3JW0McfO0b43GiCa4pvp/T+LvNHr8s4b4xBQCm6rLKPLuB3VG8=
11
- =ZWLy
5
+ iQEcBAABAgAGBQJUlA2PAAoJECrHRaUKISqMr/wH/32meSRiPtVaiDcVmbacmRXn
6
+ iZ7KrTWZ1PqA03DpZBSDc/aZmpIQCR/kbt/Ozq5bEar102V8XuJ9iM9l4LNeiRBx
7
+ iOXAj2/F8hRAR4eUPjNeMmrDFnoGF250aBTSjzr846DQZGUxB3PwDIPxeKMS96ek
8
+ eD70rQ5BYzb1yfsxgcRMnSEQtjGv5je+t13c7gjwfERhpkBPquEA5rboQYRnyCsD
9
+ 5ujz+VM3Tf04QMGQy66V9KiRa99BwuzkUaX62qpHdPB6K97f+2729nR0duY3pH6+
10
+ MY2XYKut8R0XIR+J9Ho6M0lHWASytT4huFDRmF7th2uxPCI2NdOg0fmdG3gyTyo=
11
+ =2PDq
12
12
  -----END PGP SIGNATURE-----