passenger 5.0.0.beta2 → 5.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (73) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/CHANGELOG +30 -0
  5. data/CONTRIBUTORS +2 -0
  6. data/Gemfile.lock +1 -1
  7. data/bin/passenger-status +13 -15
  8. data/build/cxx_tests.rb +14 -1
  9. data/build/preprocessor.rb +4 -2
  10. data/debian.template/control.template +2 -2
  11. data/doc/Security of user switching support.txt +2 -2
  12. data/doc/Users guide Apache.idmap.txt +6 -4
  13. data/doc/Users guide Apache.txt +20 -1
  14. data/doc/Users guide Nginx.idmap.txt +5 -3
  15. data/doc/Users guide Nginx.txt +22 -2
  16. data/ext/apache2/Configuration.cpp +6 -0
  17. data/ext/apache2/Configuration.hpp +4 -1
  18. data/ext/apache2/Hooks.cpp +1 -0
  19. data/ext/common/Constants.h +4 -2
  20. data/ext/common/Constants.h.erb +1 -1
  21. data/ext/common/DataStructures/LString.h +10 -0
  22. data/ext/common/ServerKit/Channel.h +1 -1
  23. data/ext/common/ServerKit/Context.h +2 -21
  24. data/ext/common/ServerKit/CookieUtils.h +246 -0
  25. data/ext/common/ServerKit/FdSourceChannel.h +10 -0
  26. data/ext/common/ServerKit/FileBufferedChannel.h +173 -17
  27. data/ext/common/ServerKit/FileBufferedFdSinkChannel.h +33 -1
  28. data/ext/common/ServerKit/HeaderTable.h +3 -1
  29. data/ext/common/ServerKit/HttpServer.h +36 -8
  30. data/ext/common/ServerKit/Server.h +1 -0
  31. data/ext/common/Utils.cpp +2 -1
  32. data/ext/common/Utils/DateParsing.h +15 -2
  33. data/ext/common/Utils/JsonUtils.h +39 -1
  34. data/ext/common/agents/HelperAgent/Main.cpp +4 -2
  35. data/ext/common/agents/HelperAgent/OptionParser.h +14 -2
  36. data/ext/common/agents/HelperAgent/RequestHandler.h +22 -8
  37. data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +92 -11
  38. data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +3 -1
  39. data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +9 -5
  40. data/ext/common/agents/HelperAgent/RequestHandler/Request.h +1 -0
  41. data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +27 -13
  42. data/ext/common/agents/HelperAgent/ResponseCache.h +91 -34
  43. data/ext/common/agents/LoggingAgent/AdminServer.h +21 -1
  44. data/ext/nginx/CacheLocationConfig.c +20 -0
  45. data/ext/nginx/Configuration.c +130 -24
  46. data/ext/nginx/Configuration.h +2 -1
  47. data/ext/nginx/ConfigurationCommands.c +10 -0
  48. data/ext/nginx/ConfigurationFields.h +2 -0
  49. data/ext/nginx/ContentHandler.c +1 -6
  50. data/ext/nginx/CreateLocationConfig.c +5 -0
  51. data/ext/nginx/MergeLocationConfig.c +6 -0
  52. data/ext/nginx/StaticContentHandler.c +3 -9
  53. data/ext/nginx/ngx_http_passenger_module.c +2 -1
  54. data/ext/ruby/extconf.rb +5 -4
  55. data/lib/phusion_passenger.rb +2 -2
  56. data/lib/phusion_passenger/constants.rb +2 -1
  57. data/lib/phusion_passenger/nginx/config_options.rb +5 -1
  58. data/lib/phusion_passenger/rack/thread_handler_extension.rb +3 -1
  59. data/lib/phusion_passenger/ruby_core_enhancements.rb +3 -4
  60. data/lib/phusion_passenger/standalone/start_command.rb +5 -1
  61. data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +10 -3
  62. data/resources/templates/standalone/config.erb +2 -1
  63. data/test/cxx/DateParsingTest.cpp +75 -0
  64. data/test/cxx/ResponseCacheTest.cpp +322 -0
  65. data/test/cxx/ServerKit/CookieUtilsTest.cpp +274 -0
  66. data/test/cxx/ServerKit/HttpServerTest.cpp +77 -0
  67. data/test/stub/rails3.0/Gemfile.lock +2 -2
  68. data/test/stub/rails3.1/Gemfile.lock +2 -2
  69. data/test/stub/rails3.2/Gemfile.lock +2 -2
  70. data/test/stub/rails4.0/Gemfile.lock +2 -2
  71. data/test/stub/rails4.1/Gemfile.lock +2 -2
  72. metadata +6 -2
  73. metadata.gz.asc +7 -7
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2011-2014 Phusion
3
+ * Copyright (c) 2011-2015 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -68,6 +68,7 @@ onRequestObjectCreated(Client *client, Request *req) {
68
68
  virtual void deinitializeClient(Client *client) {
69
69
  ParentClass::deinitializeClient(client);
70
70
  client->output.setBuffersFlushedCallback(NULL);
71
+ client->output.setDataFlushedCallback(getClientOutputDataFlushedCallback());
71
72
  }
72
73
 
73
74
  virtual void reinitializeRequest(Client *client, Request *req) {
@@ -90,6 +91,7 @@ virtual void reinitializeRequest(Client *client, Request *req) {
90
91
  req->bodyBytesBuffered = 0;
91
92
  req->cacheKey = HashedStaticString();
92
93
  req->cacheControl = NULL;
94
+ req->varyCookie = NULL;
93
95
 
94
96
  #ifdef DEBUG_RH_EVENT_LOOP_BLOCKING
95
97
  req->timedAppPoolGet = false;
@@ -137,25 +137,29 @@ initializeFlags(Client *client, Request *req, RequestAnalysis &analysis) {
137
137
 
138
138
  bool
139
139
  respondFromTurboCache(Client *client, Request *req) {
140
- if (!turboCaching.isEnabled() || !turboCaching.responseCache.prepareRequest(req)) {
140
+ if (!turboCaching.isEnabled() || !turboCaching.responseCache.prepareRequest(this, req)) {
141
141
  return false;
142
142
  }
143
143
 
144
- SKC_TRACE(client, 2, "Turbocaching: trying to reply from cache");
144
+ SKC_TRACE(client, 2, "Turbocaching: trying to reply from cache (key \"" <<
145
+ cEscapeString(req->cacheKey) << "\")");
145
146
  SKC_TRACE(client, 2, "Turbocache entries:\n" << turboCaching.responseCache.inspect());
146
147
 
147
148
  if (turboCaching.responseCache.requestAllowsFetching(req)) {
148
149
  ResponseCache<Request>::Entry entry(turboCaching.responseCache.fetch(req,
149
150
  ev_now(getLoop())));
150
151
  if (entry.valid()) {
151
- SKC_TRACE(client, 2, "Turbocaching: cache hit (key " << req->cacheKey << ")");
152
+ SKC_TRACE(client, 2, "Turbocaching: cache hit (key \"" <<
153
+ cEscapeString(req->cacheKey) << "\")");
152
154
  turboCaching.writeResponse(this, client, req, entry);
153
155
  if (!req->ended()) {
154
156
  endRequest(&client, &req);
155
157
  }
156
158
  return true;
157
159
  } else {
158
- SKC_TRACE(client, 2, "Turbocaching: cache miss (key " << req->cacheKey << ")");
160
+ SKC_TRACE(client, 2, "Turbocaching: cache miss: " <<
161
+ entry.getCacheMissReasonString() <<
162
+ " (key \"" << cEscapeString(req->cacheKey) << "\")");
159
163
  return false;
160
164
  }
161
165
  } else {
@@ -428,7 +432,7 @@ getStickySessionCookieName(Request *req) {
428
432
  const LString *value = req->headers.lookup(PASSENGER_STICKY_SESSIONS_COOKIE_NAME);
429
433
  if (value == NULL || value->size == 0) {
430
434
  return psg_lstr_create(req->pool,
431
- P_STATIC_STRING(DEFAULT_STICKY_SESSIONS_COOKIE_NAME));
435
+ defaultStickySessionsCookieName);
432
436
  } else {
433
437
  return value;
434
438
  }
@@ -90,6 +90,7 @@ public:
90
90
 
91
91
  HashedStaticString cacheKey;
92
92
  LString *cacheControl;
93
+ LString *varyCookie;
93
94
 
94
95
  #ifdef DEBUG_RH_EVENT_LOOP_BLOCKING
95
96
  bool timedAppPoolGet;
@@ -87,8 +87,8 @@ struct SessionProtocolWorkingState {
87
87
  const LString *remoteAddr;
88
88
  const LString *remotePort;
89
89
  const LString *remoteUser;
90
- const LString *contentLength;
91
90
  const LString *contentType;
91
+ const LString *contentLength;
92
92
  bool hasBaseURI;
93
93
  };
94
94
 
@@ -177,8 +177,12 @@ determineHeaderSizeForSessionProtocol(Request *req,
177
177
  state.remoteAddr = req->secureHeaders.lookup(REMOTE_ADDR);
178
178
  state.remotePort = req->secureHeaders.lookup(REMOTE_PORT);
179
179
  state.remoteUser = req->secureHeaders.lookup(REMOTE_USER);
180
- state.contentLength = req->headers.lookup(HTTP_CONTENT_LENGTH);
181
180
  state.contentType = req->headers.lookup(HTTP_CONTENT_TYPE);
181
+ if (req->hasBody()) {
182
+ state.contentLength = req->headers.lookup(HTTP_CONTENT_LENGTH);
183
+ } else {
184
+ state.contentLength = NULL;
185
+ }
182
186
 
183
187
  dataSize += sizeof("REQUEST_URI");
184
188
  dataSize += req->path.size + 1;
@@ -246,16 +250,16 @@ determineHeaderSizeForSessionProtocol(Request *req,
246
250
  dataSize += state.remoteUser->size + 1;
247
251
  }
248
252
 
249
- if (state.contentLength != NULL) {
250
- dataSize += sizeof("CONTENT_LENGTH");
251
- dataSize += state.contentLength->size + 1;
252
- }
253
-
254
253
  if (state.contentType != NULL) {
255
254
  dataSize += sizeof("CONTENT_TYPE");
256
255
  dataSize += state.contentType->size + 1;
257
256
  }
258
257
 
258
+ if (state.contentLength != NULL) {
259
+ dataSize += sizeof("CONTENT_LENGTH");
260
+ dataSize += state.contentLength->size + 1;
261
+ }
262
+
259
263
  dataSize += sizeof("PASSENGER_CONNECT_PASSWORD");
260
264
  dataSize += req->session->getGroupSecret().size() + 1;
261
265
 
@@ -269,6 +273,11 @@ determineHeaderSizeForSessionProtocol(Request *req,
269
273
  dataSize += req->options.transaction->getTxnId().size() + 1;
270
274
  }
271
275
 
276
+ if (req->upgraded()) {
277
+ dataSize += sizeof("HTTP_CONNECTION");
278
+ dataSize += sizeof("upgrade");
279
+ }
280
+
272
281
  ServerKit::HeaderTable::Iterator it(req->headers);
273
282
  while (*it != NULL) {
274
283
  dataSize += sizeof("HTTP_") - 1 + it->header->key.size + 1;
@@ -349,18 +358,18 @@ constructHeaderForSessionProtocol(Request *req, char * restrict buffer, unsigned
349
358
  pos = appendData(pos, end, "", 1);
350
359
  }
351
360
 
352
- if (state.contentLength != NULL) {
353
- pos = appendData(pos, end, P_STATIC_STRING_WITH_NULL("CONTENT_LENGTH"));
354
- pos = appendData(pos, end, state.contentLength);
355
- pos = appendData(pos, end, "", 1);
356
- }
357
-
358
361
  if (state.contentType != NULL) {
359
362
  pos = appendData(pos, end, P_STATIC_STRING_WITH_NULL("CONTENT_TYPE"));
360
363
  pos = appendData(pos, end, state.contentType);
361
364
  pos = appendData(pos, end, "", 1);
362
365
  }
363
366
 
367
+ if (state.contentLength != NULL) {
368
+ pos = appendData(pos, end, P_STATIC_STRING_WITH_NULL("CONTENT_LENGTH"));
369
+ pos = appendData(pos, end, state.contentLength);
370
+ pos = appendData(pos, end, "", 1);
371
+ }
372
+
364
373
  pos = appendData(pos, end, P_STATIC_STRING_WITH_NULL("PASSENGER_CONNECT_PASSWORD"));
365
374
  pos = appendData(pos, end, req->session->getGroupSecret());
366
375
  pos = appendData(pos, end, "", 1);
@@ -376,6 +385,11 @@ constructHeaderForSessionProtocol(Request *req, char * restrict buffer, unsigned
376
385
  pos = appendData(pos, end, "", 1);
377
386
  }
378
387
 
388
+ if (req->upgraded()) {
389
+ pos = appendData(pos, end, P_STATIC_STRING_WITH_NULL("HTTP_CONNECTION"));
390
+ pos = appendData(pos, end, P_STATIC_STRING_WITH_NULL("upgrade"));
391
+ }
392
+
379
393
  ServerKit::HeaderTable::Iterator it(req->headers);
380
394
  while (*it != NULL) {
381
395
  if ((it->header->hash == HTTP_CONTENT_LENGTH.hash()
@@ -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
  *
@@ -31,13 +31,18 @@
31
31
  #include <cstring>
32
32
  #include <DataStructures/HashedStaticString.h>
33
33
  #include <ServerKit/http_parser.h>
34
+ #include <ServerKit/CookieUtils.h>
34
35
  #include <StaticString.h>
35
36
  #include <Utils/DateParsing.h>
36
37
  #include <Utils/StrIntUtils.h>
37
38
 
38
39
  namespace Passenger {
39
40
 
40
-
41
+ /**
42
+ * Relevant RFCs:
43
+ * https://tools.ietf.org/html/rfc7234 HTTP 1.1 Caching
44
+ * https://tools.ietf.org/html/rfc2109 HTTP State Management Mechanism
45
+ */
41
46
  template<typename Request>
42
47
  class ResponseCache {
43
48
  public:
@@ -73,6 +78,10 @@ public:
73
78
  unsigned int index;
74
79
  Header *header;
75
80
  Body *body;
81
+ enum {
82
+ NOT_FOUND,
83
+ NOT_FRESH
84
+ } cacheMissReason;
76
85
 
77
86
  Entry()
78
87
  : index(0),
@@ -90,29 +99,48 @@ public:
90
99
  bool valid() const {
91
100
  return header != NULL;
92
101
  }
102
+
103
+ const char *getCacheMissReasonString() const {
104
+ switch (cacheMissReason) {
105
+ case NOT_FOUND:
106
+ return "NOT_FOUND";
107
+ case NOT_FRESH:
108
+ return "NOT_FRESH";
109
+ default:
110
+ return "UNKNOWN";
111
+ }
112
+ }
93
113
  };
94
114
 
95
115
  private:
96
116
  HashedStaticString HOST;
97
117
  HashedStaticString CACHE_CONTROL;
98
118
  HashedStaticString PRAGMA_CONST;
119
+ HashedStaticString AUTHORIZATION;
99
120
  HashedStaticString VARY;
121
+ HashedStaticString WWW_AUTHENTICATE;
100
122
  HashedStaticString EXPIRES;
101
123
  HashedStaticString LAST_MODIFIED;
102
124
  HashedStaticString LOCATION;
103
125
  HashedStaticString CONTENT_LOCATION;
126
+ HashedStaticString COOKIE;
127
+ HashedStaticString PASSENGER_VARY_TURBOCACHE_BY_COOKIE;
104
128
 
105
129
  unsigned int fetches, hits, stores, storeSuccesses;
106
130
 
107
131
  Header headers[MAX_ENTRIES];
108
132
  Body bodies[MAX_ENTRIES];
109
133
 
110
- unsigned int calculateKeyLength(const LString * restrict host, const StaticString &path) {
134
+ unsigned int calculateKeyLength(const LString * restrict host,
135
+ const LString * restrict varyCookie,
136
+ const StaticString &path)
137
+ {
111
138
  unsigned int size =
112
139
  1 // protocol flag
113
140
  + ((host != NULL) ? host->size : 0)
114
- + 1 // ':'
115
- + path.size();
141
+ + 1 // '\n'
142
+ + path.size()
143
+ + ((varyCookie != NULL) ? (varyCookie->size + 1) : 0);
116
144
  if (size > MAX_KEY_LENGTH) {
117
145
  return 0;
118
146
  } else {
@@ -121,7 +149,9 @@ private:
121
149
  }
122
150
 
123
151
  void generateKey(bool https, const StaticString &path,
124
- const LString * restrict host, char * restrict output,
152
+ const LString * restrict host,
153
+ const LString * restrict varyCookie,
154
+ char * restrict output,
125
155
  unsigned int size)
126
156
  {
127
157
  char *pos = output;
@@ -142,8 +172,17 @@ private:
142
172
  }
143
173
  }
144
174
 
145
- pos = appendData(pos, end, ":", 1);
175
+ pos = appendData(pos, end, "\n", 1);
146
176
  pos = appendData(pos, end, path);
177
+
178
+ if (varyCookie != NULL) {
179
+ pos = appendData(pos, end, "\n", 1);
180
+ part = varyCookie->start;
181
+ while (part != NULL) {
182
+ pos = appendData(pos, end, part->data, part->size);
183
+ part = part->next;
184
+ }
185
+ }
147
186
  }
148
187
 
149
188
  bool statusCodeIsCacheableByDefault(unsigned int code) const {
@@ -194,10 +233,6 @@ private:
194
233
  headers[index].valid = false;
195
234
  }
196
235
 
197
- time_t parsedDateToTimestamp(struct tm &tm, int zone) const {
198
- return mktime(&tm) - zone / 100 * 60 * 60 - zone % 100 * 60;
199
- }
200
-
201
236
  time_t parseDate(psg_pool_t *pool, const LString *date, ev_tstamp now) const {
202
237
  if (date == NULL) {
203
238
  return (time_t) now;
@@ -345,13 +380,13 @@ private:
345
380
  https = req->https;
346
381
  }
347
382
 
348
- unsigned int keySize = calculateKeyLength(req->host, path);
383
+ unsigned int keySize = calculateKeyLength(req->host, req->varyCookie, path);
349
384
  if (keySize == 0) {
350
385
  return;
351
386
  }
352
387
 
353
388
  char *key = (char *) psg_pnalloc(req->pool, keySize);
354
- generateKey(https, path, req->host, key, keySize);
389
+ generateKey(https, path, req->host, req->varyCookie, key, keySize);
355
390
 
356
391
  Entry entry(lookup(StaticString(key, keySize)));
357
392
  if (entry.valid()) {
@@ -363,11 +398,15 @@ public:
363
398
  ResponseCache()
364
399
  : CACHE_CONTROL("cache-control"),
365
400
  PRAGMA_CONST("pragma"),
401
+ AUTHORIZATION("authorization"),
366
402
  VARY("vary"),
403
+ WWW_AUTHENTICATE("www-authenticate"),
367
404
  EXPIRES("expires"),
368
405
  LAST_MODIFIED("last-modified"),
369
406
  LOCATION("location"),
370
407
  CONTENT_LOCATION("content-location"),
408
+ COOKIE("cookie"),
409
+ PASSENGER_VARY_TURBOCACHE_BY_COOKIE("!~PASSENGER_VARY_TURBOCACHE_COOKIE"),
371
410
  fetches(0),
372
411
  hits(0),
373
412
  stores(0),
@@ -430,12 +469,29 @@ public:
430
469
  *
431
470
  * @post result == !req->cacheKey.empty()
432
471
  */
433
- bool prepareRequest(Request *req) {
472
+ template<typename RequestHandler>
473
+ bool prepareRequest(RequestHandler *requestHandler, Request *req) {
434
474
  if (req->upgraded() || req->host == NULL) {
435
475
  return false;
436
476
  }
437
477
 
478
+ LString *varyCookieName = req->secureHeaders.lookup(PASSENGER_VARY_TURBOCACHE_BY_COOKIE);
479
+ if (varyCookieName == NULL && !requestHandler->defaultVaryTurbocacheByCookie.empty()) {
480
+ varyCookieName = (LString *) psg_palloc(req->pool, sizeof(LString));
481
+ psg_lstr_init(varyCookieName);
482
+ psg_lstr_append(varyCookieName, req->pool,
483
+ requestHandler->defaultVaryTurbocacheByCookie.data(),
484
+ requestHandler->defaultVaryTurbocacheByCookie.size());
485
+ }
486
+ if (varyCookieName != NULL) {
487
+ LString *cookieHeader = req->headers.lookup(COOKIE);
488
+ if (cookieHeader != NULL) {
489
+ req->varyCookie = ServerKit::findCookie(req->pool, cookieHeader, varyCookieName);
490
+ }
491
+ }
492
+
438
493
  unsigned int size = calculateKeyLength(req->host,
494
+ req->varyCookie,
439
495
  StaticString(req->path.start->data, req->path.size));
440
496
  if (size == 0) {
441
497
  req->cacheKey = HashedStaticString();
@@ -443,7 +499,7 @@ public:
443
499
  }
444
500
 
445
501
  req->cacheControl = req->headers.lookup(CACHE_CONTROL);
446
- if (req->cacheControl != NULL) {
502
+ if (req->cacheControl == NULL) {
447
503
  // hasPragmaHeader is only used by requestAllowsFetching(),
448
504
  // so if there is no Cache-Control header then it's not
449
505
  // necessary to check for the Pragma header.
@@ -452,7 +508,7 @@ public:
452
508
 
453
509
  char *key = (char *) psg_pnalloc(req->pool, size);
454
510
  generateKey(req->https, StaticString(req->path.start->data, req->path.size),
455
- req->host, key, size);
511
+ req->host, req->varyCookie, key, size);
456
512
  req->cacheKey = HashedStaticString(key, size);
457
513
  return true;
458
514
  }
@@ -481,9 +537,12 @@ public:
481
537
  return entry;
482
538
  } else {
483
539
  erase(entry.index);
484
- return Entry();
540
+ Entry result;
541
+ result.cacheMissReason = Entry::NOT_FRESH;
542
+ return result;
485
543
  }
486
544
  } else {
545
+ entry.cacheMissReason = Entry::NOT_FOUND;
487
546
  return entry;
488
547
  }
489
548
  }
@@ -492,7 +551,7 @@ public:
492
551
  // @pre prepareRequest() returned true
493
552
  OXT_FORCE_INLINE
494
553
  bool requestAllowsStoring(Request *req) const {
495
- return requestAllowsFetching(req);
554
+ return req->method != HTTP_HEAD && requestAllowsFetching(req);
496
555
  }
497
556
 
498
557
  // @pre prepareRequest() returned true
@@ -503,16 +562,6 @@ public:
503
562
 
504
563
  ServerKit::HeaderTable &respHeaders = req->appResponse.headers;
505
564
 
506
- if (req->cacheControl != NULL) {
507
- req->cacheControl = psg_lstr_make_contiguous(req->cacheControl,
508
- req->pool);
509
- StaticString cacheControl = StaticString(req->cacheControl->start->data,
510
- req->cacheControl->size);
511
- if (cacheControl.find(P_STATIC_STRING("no-store")) != string::npos) {
512
- return false;
513
- }
514
- }
515
-
516
565
  req->appResponse.cacheControl = respHeaders.lookup(CACHE_CONTROL);
517
566
  if (req->appResponse.cacheControl != NULL) {
518
567
  req->appResponse.cacheControl = psg_lstr_make_contiguous(
@@ -521,12 +570,17 @@ public:
521
570
  StaticString cacheControl = StaticString(
522
571
  req->appResponse.cacheControl->start->data,
523
572
  req->appResponse.cacheControl->size);
524
- if (cacheControl.find(P_STATIC_STRING("no-store")) != string::npos) {
573
+ if (cacheControl.find(P_STATIC_STRING("no-store")) != string::npos
574
+ || cacheControl.find(P_STATIC_STRING("private")) != string::npos)
575
+ {
525
576
  return false;
526
577
  }
527
578
  }
528
579
 
529
- if (respHeaders.lookup(VARY) != NULL) {
580
+ if (req->headers.lookup(AUTHORIZATION) != NULL
581
+ || respHeaders.lookup(VARY) != NULL
582
+ || respHeaders.lookup(WWW_AUTHENTICATE) != NULL)
583
+ {
530
584
  return false;
531
585
  }
532
586
 
@@ -548,7 +602,8 @@ public:
548
602
  req->pool);
549
603
  }
550
604
 
551
- return true;
605
+ return req->appResponse.cacheControl != NULL
606
+ || req->appResponse.expiresHeader != NULL;
552
607
  }
553
608
 
554
609
  // @pre requestAllowsStoring()
@@ -609,10 +664,12 @@ public:
609
664
  string inspect() const {
610
665
  stringstream stream;
611
666
  for (unsigned int i = 0; i < MAX_ENTRIES; i++) {
667
+ time_t expiryDate = bodies[i].expiryDate;
612
668
  stream << " #" << i << ": valid=" << headers[i].valid
613
- << ", hash=" << headers[i].hash << ", keySize="
614
- << headers[i].keySize << ", key="
615
- << StaticString(bodies[i].key, headers[i].keySize) << "\n";
669
+ << ", hash=" << headers[i].hash
670
+ << ", expiryDate=" << expiryDate
671
+ << ", keySize=" << headers[i].keySize << ", key=\""
672
+ << cEscapeString(StaticString(bodies[i].key, headers[i].keySize)) << "\"\n";
616
673
  }
617
674
  return stream.str();
618
675
  }