| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 
|  | 2 // Use of this source code is governed by a BSD-style license that can be | 
|  | 3 // found in the LICENSE file. | 
|  | 4 | 
|  | 5 #include "base/metrics/histogram_macros.h" | 
|  | 6 #include "net/filter/sdch_policy_delegate.h" | 
|  | 7 #include "net/filter/sdch_stream_source.h" | 
|  | 8 | 
|  | 9 namespace { | 
|  | 10 | 
|  | 11 const char kRefreshHtml[] = | 
|  | 12     "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>"; | 
|  | 13 | 
|  | 14 // Measures the eventual fate of the stream handled by this delegate. | 
|  | 15 enum StreamFate { | 
|  | 16   STREAM_FATE_OK, | 
|  | 17   STREAM_FATE_STOP_DECODING, | 
|  | 18   STREAM_FATE_REFRESH, | 
|  | 19   STREAM_FATE_FAIL | 
|  | 20 }; | 
|  | 21 | 
|  | 22 // Causes for the fates given above. FATE_OK is always CAUSE_NONE. | 
|  | 23 enum StreamFateCause { | 
|  | 24   STREAM_FATE_CAUSE_NONE, | 
|  | 25   STREAM_FATE_CAUSE_404, | 
|  | 26   STREAM_FATE_CAUSE_NOT_200, | 
|  | 27   STREAM_FATE_CAUSE_OLD_UNENCODED, | 
|  | 28   STREAM_FATE_CAUSE_TENTATIVE_SDCH, | 
|  | 29   STREAM_FATE_CAUSE_NO_DICTIONARY, | 
|  | 30   STREAM_FATE_CAUSE_CORRUPT_SDCH, | 
|  | 31   STREAM_FATE_CAUSE_ENCODING_LIE, | 
|  | 32   STREAM_FATE_CAUSE_MAX | 
|  | 33 }; | 
|  | 34 | 
|  | 35 }  // namespace | 
|  | 36 | 
|  | 37 namespace net { | 
|  | 38 | 
|  | 39 SdchPolicyDelegate::SdchPolicyDelegate(std::unique_ptr<Context> context) | 
|  | 40     : dictionary_requested_(false), | 
|  | 41       context_(std::move(context)), | 
|  | 42       did_meta_refresh_(false), | 
|  | 43       did_stop_decoding_(false), | 
|  | 44       did_fail_request_(false) {} | 
|  | 45 | 
|  | 46 SdchPolicyDelegate::~SdchPolicyDelegate() {} | 
|  | 47 | 
|  | 48 // Dictionary errors are often the first indication that the SDCH stream has | 
|  | 49 // become corrupt. There are many possible causes: non-200 response codes, a | 
|  | 50 // cached non-SDCH-ified reply, the server opting not to use SDCH, a cached | 
|  | 51 // SDCH-ified reply for which the SdchManager did not have the dictionary, a | 
|  | 52 // corrupted response, or a response that claims to be SDCH but isn't actually. | 
|  | 53 // These are handled here by issuing a meta-refresh or swapping to passthrough | 
|  | 54 // mode if appropriate, or failing the request if the error is unrecoverable. | 
|  | 55 bool SdchPolicyDelegate::HandleDictionaryError(SdchStreamSource* source) { | 
|  | 56   GURL url; | 
|  | 57   if (!context_->GetURL(&url)) { | 
|  | 58     did_fail_request_ = true; | 
|  | 59     return false; | 
|  | 60   } | 
|  | 61 | 
|  | 62   LogSdchDictionaryError(); | 
|  | 63 | 
|  | 64   // HTTP 404 might be an unencoded error page, so if decoding failed, pass it | 
|  | 65   // through. TODO(xunjieli): crbug.com/516773. | 
|  | 66   if (context_->GetResponseCode() == 404) { | 
|  | 67     did_stop_decoding_ = true; | 
|  | 68     source->StopDecoding(); | 
|  | 69     return true; | 
|  | 70   } | 
|  | 71 | 
|  | 72   // HTTP !200 gets a meta-refresh for HTML and a hard failure otherwise. | 
|  | 73   if (context_->GetResponseCode() != 200) | 
|  | 74     return IssueMetaRefreshIfPossible(source); | 
|  | 75 | 
|  | 76   // If this is a cached result and the source hasn't requested a dictionary, it | 
|  | 77   // probably never had a dictionary to begin and is an unencoded response from | 
|  | 78   // earlier. If the source has requested a dictionary, but there was still a | 
|  | 79   // dictionary error, then the SdchManager does not have it so this method | 
|  | 80   // needs to issue a meta-refresh to get it. | 
|  | 81   if (context_->IsCachedContent() && !dictionary_requested_) { | 
|  | 82     did_stop_decoding_ = true; | 
|  | 83     source->StopDecoding(); | 
|  | 84     return true; | 
|  | 85   } | 
|  | 86 | 
|  | 87   // Ow. The original request didn't advertise any dictionaries, but the | 
|  | 88   // response claimed to be SDCH, but did not have a plausible dictionary hash. | 
|  | 89   // There is no way to repair this situation: the original request already | 
|  | 90   // didn't advertise any dictionaries, and retrying it would likely have the | 
|  | 91   // same result. Blacklist the domain and try passing through. | 
|  | 92   if (!context_->SdchDictionariesAdvertised() && !dictionary_requested_) { | 
|  | 93     did_stop_decoding_ = true; | 
|  | 94     source->StopDecoding(); | 
|  | 95     context_->GetSdchManager()->BlacklistDomain(url, | 
|  | 96                                                 SDCH_PASSING_THROUGH_NON_SDCH); | 
|  | 97     return true; | 
|  | 98   } | 
|  | 99 | 
|  | 100   return IssueMetaRefreshIfPossible(source); | 
|  | 101 } | 
|  | 102 | 
|  | 103 // A decoding error, as opposed to a dictionary error, indicates a decompression | 
|  | 104 // failure partway through the payload of the SDCH stream, which means that the | 
|  | 105 // filter already witnessed a valid dictionary ID and successfully retrieved a | 
|  | 106 // dictionary for it. Decoding errors are not recoverable and it is not | 
|  | 107 // appropriate to stop decoding, so there are relatively few error cases here. | 
|  | 108 // | 
|  | 109 // In particular, a decoding error for an HTML payload is recoverable by issuing | 
|  | 110 // a meta-refresh, but to avoid having that happen too often, this class also | 
|  | 111 // temporarily blacklists the domain. A decoding error for a non-HTML payload is | 
|  | 112 // unrecoverable, so such an error gets a permanent blacklist entry. If the | 
|  | 113 // content was cached, no blacklisting is needed. | 
|  | 114 bool SdchPolicyDelegate::HandleDecodingError(SdchStreamSource* source) { | 
|  | 115   LogSdchDecodingError(); | 
|  | 116   return IssueMetaRefreshIfPossible(source); | 
|  | 117 } | 
|  | 118 | 
|  | 119 bool SdchPolicyDelegate::GetDictionary(const std::string& server_id, | 
|  | 120                                        const std::string** text) { | 
|  | 121   dictionary_requested_ = true; | 
|  | 122   SdchManager::DictionarySet* set = context_->SdchDictionariesAdvertised(); | 
|  | 123   if (set) { | 
|  | 124     *text = set->GetDictionaryText(server_id); | 
|  | 125     if (!(*text)->empty()) { | 
|  | 126       LOG(ERROR) << "GetDictionary success!"; | 
|  | 127       return true; | 
|  | 128     } | 
|  | 129   } | 
|  | 130   GURL url; | 
|  | 131   if (!context_->GetURL(&url)) | 
|  | 132     return false; | 
|  | 133   SdchProblemCode rv = SDCH_OK; | 
|  | 134   unexpected_dictionary_set_ = | 
|  | 135       context_->GetSdchManager()->GetDictionarySetByHash(url, server_id, &rv); | 
|  | 136   if (unexpected_dictionary_set_) { | 
|  | 137     *text = unexpected_dictionary_set_->GetDictionaryText(server_id); | 
|  | 138     // XXX: log fallback here (UNADVERTISED_DICTIONARY_USED etc) | 
|  | 139     if (!(*text)->empty()) | 
|  | 140       return true; | 
|  | 141   } | 
|  | 142   // XXX: check for HASH_NOT_FOUND / HASH_MALFORMED here? or in | 
|  | 143   // SdchStreamSource? | 
|  | 144   return false; | 
|  | 145 } | 
|  | 146 | 
|  | 147 void SdchPolicyDelegate::NotifyStreamDone() { | 
|  | 148 // Don't understand what to do with these. | 
|  | 149 #if 0 | 
|  | 150   if (did_fail_request_) | 
|  | 151     UMA_HISTOGRAM_COUNTS("Sdch4.StreamFate", STREAM_FATE_FAIL); | 
|  | 152   else if (did_meta_refresh_) | 
|  | 153     UMA_HISTOGRAM_COUNTS("Sdch4.StreamFate", STREAM_FATE_REFRESH); | 
|  | 154   else if (did_stop_decoding_) | 
|  | 155     UMA_HISTOGRAM_COUNTS("Sdch4.StreamFate", STREAM_FATE_STOP_DECODING); | 
|  | 156   else | 
|  | 157     UMA_HISTOGRAM_COUNTS("Sdch4.StreamFate", STREAM_FATE_OK); | 
|  | 158 #endif | 
|  | 159 } | 
|  | 160 | 
|  | 161 bool SdchPolicyDelegate::IssueMetaRefreshIfPossible(SdchStreamSource* source) { | 
|  | 162   std::string mime_type; | 
|  | 163   if (!context_->GetMimeType(&mime_type)) { | 
|  | 164     did_fail_request_ = true; | 
|  | 165     return false; | 
|  | 166   } | 
|  | 167   GURL url; | 
|  | 168   if (!context_->GetURL(&url)) { | 
|  | 169     did_fail_request_ = true; | 
|  | 170     return false; | 
|  | 171   } | 
|  | 172   SdchManager* manager = context_->GetSdchManager(); | 
|  | 173 | 
|  | 174   // Errors for non-HTML payloads are unrecoverable and get the domain | 
|  | 175   // blacklisted indefinitely. | 
|  | 176   if (mime_type.npos == mime_type.find("text/html")) { | 
|  | 177     SdchProblemCode problem = | 
|  | 178         (context_->IsCachedContent() ? SDCH_CACHED_META_REFRESH_UNSUPPORTED | 
|  | 179                                      : SDCH_META_REFRESH_UNSUPPORTED); | 
|  | 180     manager->BlacklistDomainForever(url, problem); | 
|  | 181     did_fail_request_ = true; | 
|  | 182     return false; | 
|  | 183   } | 
|  | 184 | 
|  | 185   if (!context_->IsCachedContent()) | 
|  | 186     manager->BlacklistDomain(url, SDCH_META_REFRESH_RECOVERY); | 
|  | 187 | 
|  | 188   source->ReplaceOutput(kRefreshHtml, strlen(kRefreshHtml)); | 
|  | 189   did_meta_refresh_ = true; | 
|  | 190   return true; | 
|  | 191 } | 
|  | 192 | 
|  | 193 // Old histograms: Sdch3. | 
|  | 194 //   FilterUseBeforeDisabling | 
|  | 195 //   PartialBytesIn, PartialVcdiffIn, PartialVcdiffOut | 
|  | 196 //   UnflushedBytesIn, UnflushedBufferSize, UnflushedVcdiffIn, | 
|  | 197 //   UnflushedVcdiffOut | 
|  | 198 //   Network_Decode_Ratio_a | 
|  | 199 //   Network_Decode_Bytes_VcdiffOut_a | 
|  | 200 //   NetworkBytesSavedByCompression | 
|  | 201 //   ResponseCorruptionDetection.{Cached,Uncached} | 
|  | 202 | 
|  | 203 // Decoding errors only produce Sdch3.ResponseCorruptionDetection histograms. To | 
|  | 204 // have a decoding error, the dictionary header must have been valid, and the | 
|  | 205 // dictionary must have been loaded successfully, but the response doesn't | 
|  | 206 // decode successfully, so it is corrupt. | 
|  | 207 void SdchPolicyDelegate::LogSdchDecodingError() { | 
|  | 208   if (context_->IsCachedContent()) { | 
|  | 209     UMA_HISTOGRAM_ENUMERATION("Sdch3.ResponseCorruptionDetection.Cached", | 
|  | 210                               STREAM_FATE_CAUSE_CORRUPT_SDCH, | 
|  | 211                               STREAM_FATE_CAUSE_MAX); | 
|  | 212   } else { | 
|  | 213     UMA_HISTOGRAM_ENUMERATION("Sdch3.ResponseCorruptionDetection.Uncached", | 
|  | 214                               STREAM_FATE_CAUSE_CORRUPT_SDCH, | 
|  | 215                               STREAM_FATE_CAUSE_MAX); | 
|  | 216   } | 
|  | 217 } | 
|  | 218 | 
|  | 219 void SdchPolicyDelegate::LogSdchDictionaryError() { | 
|  | 220   std::string mime_type; | 
|  | 221   if (!context_->GetMimeType(&mime_type)) | 
|  | 222     return; | 
|  | 223   // TODO(ellyjones): figure out what stats to gather here | 
|  | 224 } | 
|  | 225 | 
|  | 226 }  // namespace net | 
| OLD | NEW | 
|---|