• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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 "net/http/partial_data.h"
6 
7 #include <limits>
8 #include <utility>
9 
10 #include "base/format_macros.h"
11 #include "base/functional/bind.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/logging.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "net/base/net_errors.h"
18 #include "net/disk_cache/disk_cache.h"
19 #include "net/http/http_response_headers.h"
20 #include "net/http/http_status_code.h"
21 #include "net/http/http_util.h"
22 
23 namespace net {
24 
25 namespace {
26 
27 // The headers that we have to process.
28 const char kLengthHeader[] = "Content-Length";
29 const char kRangeHeader[] = "Content-Range";
30 const int kDataStream = 1;
31 
32 }  // namespace
33 
34 PartialData::PartialData() = default;
35 
36 PartialData::~PartialData() = default;
37 
Init(const HttpRequestHeaders & headers)38 bool PartialData::Init(const HttpRequestHeaders& headers) {
39   std::optional<std::string> range_header =
40       headers.GetHeader(HttpRequestHeaders::kRange);
41   if (!range_header) {
42     range_requested_ = false;
43     return false;
44   }
45   range_requested_ = true;
46 
47   std::vector<HttpByteRange> ranges;
48   if (!HttpUtil::ParseRangeHeader(range_header.value(), &ranges) ||
49       ranges.size() != 1) {
50     return false;
51   }
52 
53   // We can handle this range request.
54   byte_range_ = ranges[0];
55   user_byte_range_ = byte_range_;
56   if (!byte_range_.IsValid())
57     return false;
58 
59   current_range_start_ = byte_range_.first_byte_position();
60 
61   DVLOG(1) << "Range start: " << current_range_start_ << " end: " <<
62                byte_range_.last_byte_position();
63   return true;
64 }
65 
SetHeaders(const HttpRequestHeaders & headers)66 void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
67   DCHECK(extra_headers_.IsEmpty());
68   extra_headers_ = headers;
69 }
70 
RestoreHeaders(HttpRequestHeaders * headers) const71 void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const {
72   DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
73   int64_t end = byte_range_.IsSuffixByteRange()
74                     ? byte_range_.suffix_length()
75                     : byte_range_.last_byte_position();
76 
77   *headers = extra_headers_;
78   if (truncated_ || !byte_range_.IsValid())
79     return;
80 
81   if (current_range_start_ < 0) {
82     headers->SetHeader(HttpRequestHeaders::kRange,
83                        HttpByteRange::Suffix(end).GetHeaderValue());
84   } else {
85     headers->SetHeader(HttpRequestHeaders::kRange,
86                        HttpByteRange::Bounded(
87                            current_range_start_, end).GetHeaderValue());
88   }
89 }
90 
ShouldValidateCache(disk_cache::Entry * entry,CompletionOnceCallback callback)91 int PartialData::ShouldValidateCache(disk_cache::Entry* entry,
92                                      CompletionOnceCallback callback) {
93   DCHECK_GE(current_range_start_, 0);
94 
95   // Scan the disk cache for the first cached portion within this range.
96   int len = GetNextRangeLen();
97   if (!len)
98     return 0;
99 
100   DVLOG(3) << "ShouldValidateCache len: " << len;
101 
102   if (sparse_entry_) {
103     DCHECK(callback_.is_null());
104     disk_cache::RangeResultCallback cb = base::BindOnce(
105         &PartialData::GetAvailableRangeCompleted, weak_factory_.GetWeakPtr());
106     disk_cache::RangeResult range =
107         entry->GetAvailableRange(current_range_start_, len, std::move(cb));
108 
109     cached_min_len_ =
110         range.net_error == OK ? range.available_len : range.net_error;
111     if (cached_min_len_ == ERR_IO_PENDING) {
112       callback_ = std::move(callback);
113       return ERR_IO_PENDING;
114     } else {
115       cached_start_ = range.start;
116     }
117   } else if (!truncated_) {
118     if (byte_range_.HasFirstBytePosition() &&
119         byte_range_.first_byte_position() >= resource_size_) {
120       // The caller should take care of this condition because we should have
121       // failed IsRequestedRangeOK(), but it's better to be consistent here.
122       len = 0;
123     }
124     cached_min_len_ = len;
125     cached_start_ = current_range_start_;
126   }
127 
128   if (cached_min_len_ < 0)
129     return cached_min_len_;
130 
131   // Return a positive number to indicate success (versus error or finished).
132   return 1;
133 }
134 
PrepareCacheValidation(disk_cache::Entry * entry,HttpRequestHeaders * headers)135 void PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
136                                          HttpRequestHeaders* headers) {
137   DCHECK_GE(current_range_start_, 0);
138   DCHECK_GE(cached_min_len_, 0);
139 
140   int len = GetNextRangeLen();
141   if (!len) {
142     // Stored body is empty, so just use the original range header.
143     headers->SetHeader(HttpRequestHeaders::kRange,
144                        user_byte_range_.GetHeaderValue());
145     return;
146   }
147   range_present_ = false;
148 
149   *headers = extra_headers_;
150 
151   if (!cached_min_len_) {
152     // We don't have anything else stored.
153     final_range_ = true;
154     cached_start_ =
155         byte_range_.HasLastBytePosition() ? current_range_start_  + len : 0;
156   }
157 
158   if (current_range_start_ == cached_start_) {
159     // The data lives in the cache.
160     range_present_ = true;
161     current_range_end_ = cached_start_ + cached_min_len_ - 1;
162     if (len == cached_min_len_)
163       final_range_ = true;
164   } else {
165     // This range is not in the cache.
166     current_range_end_ = cached_start_ - 1;
167   }
168   headers->SetHeader(
169       HttpRequestHeaders::kRange,
170       HttpByteRange::Bounded(current_range_start_, current_range_end_)
171           .GetHeaderValue());
172 }
173 
IsCurrentRangeCached() const174 bool PartialData::IsCurrentRangeCached() const {
175   return range_present_;
176 }
177 
IsLastRange() const178 bool PartialData::IsLastRange() const {
179   return final_range_;
180 }
181 
UpdateFromStoredHeaders(const HttpResponseHeaders * headers,disk_cache::Entry * entry,bool truncated,bool writing_in_progress)182 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
183                                           disk_cache::Entry* entry,
184                                           bool truncated,
185                                           bool writing_in_progress) {
186   resource_size_ = 0;
187   if (truncated) {
188     DCHECK_EQ(headers->response_code(), 200);
189     // We don't have the real length and the user may be trying to create a
190     // sparse entry so let's not write to this entry.
191     if (byte_range_.IsValid())
192       return false;
193 
194     if (!headers->HasStrongValidators())
195       return false;
196 
197     // Now we avoid resume if there is no content length, but that was not
198     // always the case so double check here.
199     int64_t total_length = headers->GetContentLength();
200     if (total_length <= 0)
201       return false;
202 
203     // In case we see a truncated entry, we first send a network request for
204     // 1 byte range with If-Range: to probe server support for resumption.
205     // The setting of |current_range_start_| and |cached_start_| below (with any
206     // positive value of |cached_min_len_|) results in that.
207     //
208     // Setting |initial_validation_| to true is how this communicates to
209     // HttpCache::Transaction that we're doing that (and that it's not the user
210     // asking for one byte), so if it sees a 206 with that flag set it will call
211     // SetRangeToStartDownload(), and then restart the process looking for the
212     // entire file (which is what the user wanted), with the cache handling
213     // the previous portion, and then a second network request for the entire
214     // rest of the range. A 200 in response to the probe request can be simply
215     // returned directly to the user.
216     truncated_ = true;
217     initial_validation_ = true;
218     sparse_entry_ = false;
219     int current_len = entry->GetDataSize(kDataStream);
220     byte_range_.set_first_byte_position(current_len);
221     resource_size_ = total_length;
222     current_range_start_ = current_len;
223     cached_min_len_ = current_len;
224     cached_start_ = current_len + 1;
225     return true;
226   }
227 
228   sparse_entry_ = (headers->response_code() == HTTP_PARTIAL_CONTENT);
229 
230   if (writing_in_progress || sparse_entry_) {
231     // |writing_in_progress| means another Transaction is still fetching the
232     // body, so the only way we can see the length is if the server sent it
233     // in Content-Length -- GetDataSize would just return what got written
234     // thus far.
235     //
236     // |sparse_entry_| means a 206, and for those FixContentLength arranges it
237     // so that Content-Length written to the cache has the full length (on wire
238     // it's for a particular range only); while GetDataSize would be unusable
239     // since the data is stored using WriteSparseData, and not in the usual data
240     // stream.
241     resource_size_ = headers->GetContentLength();
242     if (resource_size_ <= 0)
243       return false;
244   } else {
245     // If we can safely use GetDataSize, it's preferrable since it's usable for
246     // things w/o Content-Length, such as chunked content.
247     resource_size_ = entry->GetDataSize(kDataStream);
248   }
249 
250   DVLOG(2) << "UpdateFromStoredHeaders size: " << resource_size_;
251 
252   if (sparse_entry_) {
253     // If our previous is a 206, we need strong validators as we may be
254     // stiching the cached data and network data together.
255     if (!headers->HasStrongValidators())
256       return false;
257     // Make sure that this is really a sparse entry.
258     return entry->CouldBeSparse();
259   }
260   return true;
261 }
262 
SetRangeToStartDownload()263 void PartialData::SetRangeToStartDownload() {
264   DCHECK(truncated_);
265   DCHECK(!sparse_entry_);
266   current_range_start_ = 0;
267   cached_start_ = 0;
268   initial_validation_ = false;
269 }
270 
IsRequestedRangeOK()271 bool PartialData::IsRequestedRangeOK() {
272   if (byte_range_.IsValid()) {
273     if (!byte_range_.ComputeBounds(resource_size_))
274       return false;
275     if (truncated_)
276       return true;
277 
278     if (current_range_start_ < 0)
279       current_range_start_ = byte_range_.first_byte_position();
280   } else {
281     // This is not a range request but we have partial data stored.
282     current_range_start_ = 0;
283     byte_range_.set_last_byte_position(resource_size_ - 1);
284   }
285 
286   bool rv = current_range_start_ >= 0;
287   if (!rv)
288     current_range_start_ = 0;
289 
290   return rv;
291 }
292 
ResponseHeadersOK(const HttpResponseHeaders * headers)293 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
294   if (headers->response_code() == HTTP_NOT_MODIFIED) {
295     if (!byte_range_.IsValid() || truncated_)
296       return true;
297 
298     // We must have a complete range here.
299     return byte_range_.HasFirstBytePosition() &&
300         byte_range_.HasLastBytePosition();
301   }
302 
303   int64_t start, end, total_length;
304   if (!headers->GetContentRangeFor206(&start, &end, &total_length))
305     return false;
306   if (total_length <= 0)
307     return false;
308 
309   DCHECK_EQ(headers->response_code(), 206);
310 
311   // A server should return a valid content length with a 206 (per the standard)
312   // but relax the requirement because some servers don't do that.
313   int64_t content_length = headers->GetContentLength();
314   if (content_length > 0 && content_length != end - start + 1)
315     return false;
316 
317   if (!resource_size_) {
318     // First response. Update our values with the ones provided by the server.
319     resource_size_ = total_length;
320     if (!byte_range_.HasFirstBytePosition()) {
321       byte_range_.set_first_byte_position(start);
322       current_range_start_ = start;
323     }
324     if (!byte_range_.HasLastBytePosition())
325       byte_range_.set_last_byte_position(end);
326   } else if (resource_size_ != total_length) {
327     return false;
328   }
329 
330   if (truncated_) {
331     if (!byte_range_.HasLastBytePosition())
332       byte_range_.set_last_byte_position(end);
333   }
334 
335   if (start != current_range_start_)
336     return false;
337 
338   if (!current_range_end_) {
339     // There is nothing in the cache.
340     DCHECK(byte_range_.HasLastBytePosition());
341     current_range_end_ = byte_range_.last_byte_position();
342     if (current_range_end_ >= resource_size_) {
343       // We didn't know the real file size, and the server is saying that the
344       // requested range goes beyond the size. Fix it.
345       current_range_end_ = end;
346       byte_range_.set_last_byte_position(end);
347     }
348   }
349 
350   // If we received a range, but it's not exactly the range we asked for, avoid
351   // trouble and signal an error.
352   if (end != current_range_end_)
353     return false;
354 
355   return true;
356 }
357 
358 // We are making multiple requests to complete the range requested by the user.
359 // Just assume that everything is fine and say that we are returning what was
360 // requested.
FixResponseHeaders(HttpResponseHeaders * headers,bool success)361 void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
362                                      bool success) {
363   if (truncated_)
364     return;
365 
366   if (!success) {
367     headers->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable");
368     headers->SetHeader(
369         kRangeHeader, base::StringPrintf("bytes 0-0/%" PRId64, resource_size_));
370     headers->SetHeader(kLengthHeader, "0");
371     return;
372   }
373 
374   if (byte_range_.IsValid() && resource_size_) {
375     headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_);
376   } else {
377     if (headers->response_code() == HTTP_PARTIAL_CONTENT) {
378       // TODO(rvargas): Is it safe to change the protocol version?
379       headers->ReplaceStatusLine("HTTP/1.1 200 OK");
380     }
381     headers->RemoveHeader(kRangeHeader);
382     headers->SetHeader(kLengthHeader,
383                        base::StringPrintf("%" PRId64, resource_size_));
384   }
385 }
386 
FixContentLength(HttpResponseHeaders * headers)387 void PartialData::FixContentLength(HttpResponseHeaders* headers) {
388   headers->SetHeader(kLengthHeader,
389                      base::StringPrintf("%" PRId64, resource_size_));
390 }
391 
CacheRead(disk_cache::Entry * entry,IOBuffer * data,int data_len,CompletionOnceCallback callback)392 int PartialData::CacheRead(disk_cache::Entry* entry,
393                            IOBuffer* data,
394                            int data_len,
395                            CompletionOnceCallback callback) {
396   int read_len = std::min(data_len, cached_min_len_);
397   if (!read_len)
398     return 0;
399 
400   int rv = 0;
401   if (sparse_entry_) {
402     rv = entry->ReadSparseData(current_range_start_, data, read_len,
403                                std::move(callback));
404   } else {
405     if (current_range_start_ > std::numeric_limits<int32_t>::max())
406       return ERR_INVALID_ARGUMENT;
407 
408     rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
409                          data, read_len, std::move(callback));
410   }
411   return rv;
412 }
413 
CacheWrite(disk_cache::Entry * entry,IOBuffer * data,int data_len,CompletionOnceCallback callback)414 int PartialData::CacheWrite(disk_cache::Entry* entry,
415                             IOBuffer* data,
416                             int data_len,
417                             CompletionOnceCallback callback) {
418   DVLOG(3) << "To write: " << data_len;
419   if (sparse_entry_) {
420     return entry->WriteSparseData(current_range_start_, data, data_len,
421                                   std::move(callback));
422   } else  {
423     if (current_range_start_ > std::numeric_limits<int32_t>::max())
424       return ERR_INVALID_ARGUMENT;
425 
426     return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
427                             data, data_len, std::move(callback), true);
428   }
429 }
430 
OnCacheReadCompleted(int result)431 void PartialData::OnCacheReadCompleted(int result) {
432   DVLOG(3) << "Read: " << result;
433   if (result > 0) {
434     current_range_start_ += result;
435     cached_min_len_ -= result;
436     DCHECK_GE(cached_min_len_, 0);
437   }
438 }
439 
OnNetworkReadCompleted(int result)440 void PartialData::OnNetworkReadCompleted(int result) {
441   if (result > 0)
442     current_range_start_ += result;
443 }
444 
GetNextRangeLen()445 int PartialData::GetNextRangeLen() {
446   if (!resource_size_) {
447     return 0;
448   }
449   int64_t range_len =
450       byte_range_.HasLastBytePosition()
451           ? byte_range_.last_byte_position() - current_range_start_ + 1
452           : std::numeric_limits<int32_t>::max();
453   if (range_len > std::numeric_limits<int32_t>::max())
454     range_len = std::numeric_limits<int32_t>::max();
455   return static_cast<int32_t>(range_len);
456 }
457 
GetAvailableRangeCompleted(const disk_cache::RangeResult & result)458 void PartialData::GetAvailableRangeCompleted(
459     const disk_cache::RangeResult& result) {
460   DCHECK(!callback_.is_null());
461   DCHECK_NE(ERR_IO_PENDING, result.net_error);
462 
463   int len_or_error =
464       result.net_error == OK ? result.available_len : result.net_error;
465   cached_start_ = result.start;
466   cached_min_len_ = len_or_error;
467 
468   // ShouldValidateCache has an unusual convention where 0 denotes EOF,
469   // so convert end of range to success (since there may be things that need
470   // fetching from network or other ranges).
471   std::move(callback_).Run(len_or_error >= 0 ? 1 : len_or_error);
472 }
473 
474 }  // namespace net
475