| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Author: jmaessen@google.com (Jan Maessen) |
| // |
| // This contains some utilities for working with the critical_keys |
| // proto, and for updating support values. This is primarily used by |
| // CriticalSelectorFinder and CriticalImagesFinder. These finders use the |
| // critical_keys proto to store a "support value" for each possible key (image |
| // URL or selector name) in the property cache. When a beacon result arrives, |
| // the support for each critical key in the result is increased by |
| // support_interval. When a new beacon is sent, existing support is decayed by |
| // multiplying by support_interval/(support_interval+1) and rounding down. This |
| // means that a single key returned with a beacon will be considered critical |
| // until support_interval subsequent beacons have been injected. Because |
| // support decays exponentially, repeated support for a key in multiple beacon |
| // results cause that key to be considered critical longer: two beacon results |
| // will expire after somewhat less than twice as long, three after rather less |
| // than three times as long, and so forth. This class also handles converting |
| // over old protobufs that did not use the support system. |
| |
| #ifndef NET_INSTAWEB_REWRITER_PUBLIC_CRITICAL_FINDER_SUPPORT_UTIL_H_ |
| #define NET_INSTAWEB_REWRITER_PUBLIC_CRITICAL_FINDER_SUPPORT_UTIL_H_ |
| |
| #include "net/instaweb/rewriter/critical_keys.pb.h" |
| #include "net/instaweb/util/public/property_cache.h" |
| #include "pagespeed/kernel/base/basictypes.h" |
| #include "pagespeed/kernel/base/string.h" |
| #include "pagespeed/kernel/base/string_util.h" |
| #include "pagespeed/kernel/base/timer.h" |
| |
| namespace net_instaweb { |
| |
| // The amount of time after generating a nonce that we will accept it as valid. |
| // This keeps an attacker from accumulating large numbers of valid nonces to |
| // send many beacon responses at once. |
| const int64 kBeaconTimeoutIntervalMs = Timer::kMinuteMs; |
| |
| // The number of valid beacons received that will switch from high frequency to |
| // low frequency beaconing. |
| const int64 kHighFreqBeaconCount = 3; |
| |
| // The multiplier to apply to RewriteOptions::beacon_reinstrument_time_sec() to |
| // determine the low frequency beaconing interval. For example, the default |
| // value rebeaconing value is 5 seconds, so we will rebeacon every 5 seconds in |
| // high frequency mode, and every 500 seconds (~8 minutes) in low frequency |
| // mode. |
| const int64 kLowFreqBeaconMult = 100; |
| |
| // The limit on the number of nonces that can expire before we stop trying to do |
| // high frequency beaconing. This is a signal that beacons are not configured |
| // correctly and so we drop into low frequency beaconing mode. |
| const int64 kNonceExpirationLimit = 5; |
| |
| class MessageHandler; |
| class NonceGenerator; |
| class RewriteDriver; |
| |
| enum BeaconStatus { |
| kDoNotBeacon, |
| kBeaconNoNonce, |
| kBeaconWithNonce |
| }; |
| |
| struct BeaconMetadata { |
| BeaconMetadata() : status(kDoNotBeacon) { } |
| BeaconStatus status; |
| GoogleString nonce; |
| }; |
| |
| // Check whether the given nonce is valid, invalidating any expired nonce |
| // entries we might encounter. To avoid the need to copy and clear the nonce |
| // list, we invalidate the entry used and any expired entries by clearing the |
| // nonce value and timestamp. These entries will be reused by |
| // AddNonceToCriticalSelectors. |
| bool ValidateAndExpireNonce(int64 now_ms, StringPiece nonce, |
| CriticalKeys* critical_keys); |
| |
| // Generate a list of the critical keys from a proto, storing it into keys. |
| // Takes into account legacy keys that may have been added before. A key is |
| // considered critical if its support is at least support_percentage of the |
| // maximum possible support value (which ramps up as beacon results arrive). |
| // When support_percentage = 0, any support is sufficient; when |
| // support_percentage = 100 all beacon results must support criticality. |
| void GetCriticalKeysFromProto(int64 support_percentage, |
| const CriticalKeys& critical_keys, |
| StringSet* keys); |
| |
| // Add support for new_set to existing support. The new_set should be obtained |
| // from a fully-validated beacon result -- this means PrepareForBeaconInsertion |
| // should have been called if required, and the resulting nonce should have been |
| // checked. If require_prior_support then there must be an existing support |
| // entry (possibly 0) for new support to be registered. |
| void UpdateCriticalKeys(bool require_prior_support, |
| const StringSet& new_set, int support_value, |
| CriticalKeys* critical_keys); |
| |
| bool ShouldBeacon(int64 next_beacon_timestamp_ms, const RewriteDriver& driver); |
| |
| enum CriticalKeysWriteFlags { |
| kNoRequirementsOnPriorResult = 0, // Nice name for lack of next two flags. |
| kReplacePriorResult = 1, |
| kRequirePriorSupport = 2, |
| kSkipNonceCheck = 4 |
| }; |
| |
| // Update the property cache with a new set of keys. This will update the |
| // support value for the new keys. If require_prior_support is set, any keys |
| // that are not already present in the property cache will be ignored (to |
| // prevent spurious keys from being injected). Note that it only increases the |
| // support value for the new keys, it does not decay values that are not |
| // present. PrepareForBeaconInsertion should have been called previously if |
| // !should_replace_prior_result and nonces must be checked. |
| void WriteCriticalKeysToPropertyCache( |
| const StringSet& new_keys, StringPiece nonce, int support_interval, |
| CriticalKeysWriteFlags flags, |
| StringPiece property_name, const PropertyCache* cache, |
| const PropertyCache::Cohort* cohort, AbstractPropertyPage* page, |
| MessageHandler* message_handler, Timer* timer); |
| |
| // Given a set of candidate critical keys, decide whether beaconing should take |
| // place. We should *always* beacon if there's new critical key data. Otherwise |
| // re-beaconing is based on a time and request interval, and 2 modes of |
| // beaconing frequency are supported. At first, beaconing occurs at a |
| // high frequency until we have collected kHighFreqBeaconCount beacons; after |
| // that, we transition into low frequency beaconing mode, where beaconing occurs |
| // less often. We also track the number of expired nonces since the last valid |
| // beacon was received to see if beaconing is set up correctly, and if it looks |
| // like it isn't, only do low frequency beaconing. Sets status and nonce |
| // appropriately in *result (nonce will be empty if no nonce is required). If |
| // candidate keys are not required, keys may be empty (but new candidate |
| // detection will not occur). If result->status != kDontBeacon, caller should |
| // write proto back to the property cache using UpdateInPropertyCache. |
| void PrepareForBeaconInsertionHelper(CriticalKeys* proto, |
| NonceGenerator* nonce_generator, |
| RewriteDriver* driver, |
| bool using_candidate_key_detection, |
| BeaconMetadata* result); |
| |
| // Update the candidate key set in proto. If new candidate keys are detected, |
| // they are inserted into proto with a support value of 0, and true is returned. |
| // Otherwise returns false. If clear_rebeacon_timestamp is set, the rebeacon |
| // timestamp field in the proto is cleared to force rebeaconing on the next |
| // request. |
| bool UpdateCandidateKeys(const StringSet& keys, CriticalKeys* proto, |
| bool clear_rebeacon_timestamp); |
| |
| // Based on the CriticalKeys data seen so far, describe whether beacon metadata |
| // is available. This returns false until data is received. |
| inline bool IsBeaconDataAvailable(const CriticalKeys& proto) { |
| return (proto.valid_beacons_received() > 0); |
| } |
| |
| } // namespace net_instaweb |
| |
| #endif // NET_INSTAWEB_REWRITER_PUBLIC_CRITICAL_FINDER_SUPPORT_UTIL_H_ |