Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / net / http / partial_data.cc
1 // Copyright (c) 2012 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 "net/http/partial_data.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/format_macros.h"
10 #include "base/logging.h"
11 #include "base/profiler/scoped_tracker.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "net/base/net_errors.h"
16 #include "net/disk_cache/disk_cache.h"
17 #include "net/http/http_response_headers.h"
18 #include "net/http/http_util.h"
19
20 namespace net {
21
22 namespace {
23
24 // The headers that we have to process.
25 const char kLengthHeader[] = "Content-Length";
26 const char kRangeHeader[] = "Content-Range";
27 const int kDataStream = 1;
28
29 }  // namespace
30
31 // A core object that can be detached from the Partialdata object at destruction
32 // so that asynchronous operations cleanup can be performed.
33 class PartialData::Core {
34  public:
35   // Build a new core object. Lifetime management is automatic.
36   static Core* CreateCore(PartialData* owner) {
37     return new Core(owner);
38   }
39
40   // Wrapper for Entry::GetAvailableRange. If this method returns ERR_IO_PENDING
41   // PartialData::GetAvailableRangeCompleted() will be invoked on the owner
42   // object when finished (unless Cancel() is called first).
43   int GetAvailableRange(disk_cache::Entry* entry, int64 offset, int len,
44                         int64* start);
45
46   // Cancels a pending operation. It is a mistake to call this method if there
47   // is no operation in progress; in fact, there will be no object to do so.
48   void Cancel();
49
50  private:
51   explicit Core(PartialData* owner);
52   ~Core();
53
54   // Pending io completion routine.
55   void OnIOComplete(int result);
56
57   PartialData* owner_;
58   int64 start_;
59
60   DISALLOW_COPY_AND_ASSIGN(Core);
61 };
62
63 PartialData::Core::Core(PartialData* owner)
64     : owner_(owner), start_(0) {
65   DCHECK(!owner_->core_);
66   owner_->core_ = this;
67 }
68
69 PartialData::Core::~Core() {
70   if (owner_)
71     owner_->core_ = NULL;
72 }
73
74 void PartialData::Core::Cancel() {
75   DCHECK(owner_);
76   owner_ = NULL;
77 }
78
79 int PartialData::Core::GetAvailableRange(disk_cache::Entry* entry, int64 offset,
80                                          int len, int64* start) {
81   int rv = entry->GetAvailableRange(
82       offset, len, &start_, base::Bind(&PartialData::Core::OnIOComplete,
83                                        base::Unretained(this)));
84   if (rv != net::ERR_IO_PENDING) {
85     // The callback will not be invoked. Lets cleanup.
86     *start = start_;
87     delete this;
88   }
89   return rv;
90 }
91
92 void PartialData::Core::OnIOComplete(int result) {
93   // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
94   tracked_objects::ScopedTracker tracking_profile(
95       FROM_HERE_WITH_EXPLICIT_FUNCTION(
96           "422516 PartialData::Core::OnIOComplete"));
97
98   if (owner_)
99     owner_->GetAvailableRangeCompleted(result, start_);
100   delete this;
101 }
102
103 // -----------------------------------------------------------------------------
104
105 PartialData::PartialData()
106     : range_present_(false),
107       final_range_(false),
108       sparse_entry_(true),
109       truncated_(false),
110       initial_validation_(false),
111       core_(NULL) {
112 }
113
114 PartialData::~PartialData() {
115   if (core_)
116     core_->Cancel();
117 }
118
119 bool PartialData::Init(const HttpRequestHeaders& headers) {
120   std::string range_header;
121   if (!headers.GetHeader(HttpRequestHeaders::kRange, &range_header))
122     return false;
123
124   std::vector<HttpByteRange> ranges;
125   if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1)
126     return false;
127
128   // We can handle this range request.
129   byte_range_ = ranges[0];
130   if (!byte_range_.IsValid())
131     return false;
132
133   resource_size_ = 0;
134   current_range_start_ = byte_range_.first_byte_position();
135
136   DVLOG(1) << "Range start: " << current_range_start_ << " end: " <<
137                byte_range_.last_byte_position();
138   return true;
139 }
140
141 void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
142   DCHECK(extra_headers_.IsEmpty());
143   extra_headers_.CopyFrom(headers);
144 }
145
146 void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const {
147   DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
148   int64 end = byte_range_.IsSuffixByteRange() ?
149               byte_range_.suffix_length() : byte_range_.last_byte_position();
150
151   headers->CopyFrom(extra_headers_);
152   if (truncated_ || !byte_range_.IsValid())
153     return;
154
155   if (current_range_start_ < 0) {
156     headers->SetHeader(HttpRequestHeaders::kRange,
157                        HttpByteRange::Suffix(end).GetHeaderValue());
158   } else {
159     headers->SetHeader(HttpRequestHeaders::kRange,
160                        HttpByteRange::Bounded(
161                            current_range_start_, end).GetHeaderValue());
162   }
163 }
164
165 int PartialData::ShouldValidateCache(disk_cache::Entry* entry,
166                                      const CompletionCallback& callback) {
167   DCHECK_GE(current_range_start_, 0);
168
169   // Scan the disk cache for the first cached portion within this range.
170   int len = GetNextRangeLen();
171   if (!len)
172     return 0;
173
174   DVLOG(3) << "ShouldValidateCache len: " << len;
175
176   if (sparse_entry_) {
177     DCHECK(callback_.is_null());
178     Core* core = Core::CreateCore(this);
179     cached_min_len_ = core->GetAvailableRange(entry, current_range_start_, len,
180                                               &cached_start_);
181
182     if (cached_min_len_ == ERR_IO_PENDING) {
183       callback_ = callback;
184       return ERR_IO_PENDING;
185     }
186   } else if (!truncated_) {
187     if (byte_range_.HasFirstBytePosition() &&
188         byte_range_.first_byte_position() >= resource_size_) {
189       // The caller should take care of this condition because we should have
190       // failed IsRequestedRangeOK(), but it's better to be consistent here.
191       len = 0;
192     }
193     cached_min_len_ = len;
194     cached_start_ = current_range_start_;
195   }
196
197   if (cached_min_len_ < 0)
198     return cached_min_len_;
199
200   // Return a positive number to indicate success (versus error or finished).
201   return 1;
202 }
203
204 void PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
205                                          HttpRequestHeaders* headers) {
206   DCHECK_GE(current_range_start_, 0);
207   DCHECK_GE(cached_min_len_, 0);
208
209   int len = GetNextRangeLen();
210   DCHECK_NE(0, len);
211   range_present_ = false;
212
213   headers->CopyFrom(extra_headers_);
214
215   if (!cached_min_len_) {
216     // We don't have anything else stored.
217     final_range_ = true;
218     cached_start_ =
219         byte_range_.HasLastBytePosition() ? current_range_start_  + len : 0;
220   }
221
222   if (current_range_start_ == cached_start_) {
223     // The data lives in the cache.
224     range_present_ = true;
225     if (len == cached_min_len_)
226       final_range_ = true;
227     headers->SetHeader(
228         HttpRequestHeaders::kRange,
229         net::HttpByteRange::Bounded(
230             current_range_start_,
231             cached_start_ + cached_min_len_ - 1).GetHeaderValue());
232   } else {
233     // This range is not in the cache.
234     headers->SetHeader(
235         HttpRequestHeaders::kRange,
236         net::HttpByteRange::Bounded(
237             current_range_start_, cached_start_ - 1).GetHeaderValue());
238   }
239 }
240
241 bool PartialData::IsCurrentRangeCached() const {
242   return range_present_;
243 }
244
245 bool PartialData::IsLastRange() const {
246   return final_range_;
247 }
248
249 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
250                                           disk_cache::Entry* entry,
251                                           bool truncated) {
252   resource_size_ = 0;
253   if (truncated) {
254     DCHECK_EQ(headers->response_code(), 200);
255     // We don't have the real length and the user may be trying to create a
256     // sparse entry so let's not write to this entry.
257     if (byte_range_.IsValid())
258       return false;
259
260     if (!headers->HasStrongValidators())
261       return false;
262
263     // Now we avoid resume if there is no content length, but that was not
264     // always the case so double check here.
265     int64 total_length = headers->GetContentLength();
266     if (total_length <= 0)
267       return false;
268
269     truncated_ = true;
270     initial_validation_ = true;
271     sparse_entry_ = false;
272     int current_len = entry->GetDataSize(kDataStream);
273     byte_range_.set_first_byte_position(current_len);
274     resource_size_ = total_length;
275     current_range_start_ = current_len;
276     cached_min_len_ = current_len;
277     cached_start_ = current_len + 1;
278     return true;
279   }
280
281   if (headers->response_code() != 206) {
282     DCHECK(byte_range_.IsValid());
283     sparse_entry_ = false;
284     resource_size_ = entry->GetDataSize(kDataStream);
285     DVLOG(2) << "UpdateFromStoredHeaders size: " << resource_size_;
286     return true;
287   }
288
289   int64 length_value = headers->GetContentLength();
290   if (length_value <= 0)
291     return false;  // We must have stored the resource length.
292
293   resource_size_ = length_value;
294
295   // Make sure that this is really a sparse entry.
296   return entry->CouldBeSparse();
297 }
298
299 void PartialData::SetRangeToStartDownload() {
300   DCHECK(truncated_);
301   DCHECK(!sparse_entry_);
302   current_range_start_ = 0;
303   cached_start_ = 0;
304   initial_validation_ = false;
305 }
306
307 bool PartialData::IsRequestedRangeOK() {
308   if (byte_range_.IsValid()) {
309     if (!byte_range_.ComputeBounds(resource_size_))
310       return false;
311     if (truncated_)
312       return true;
313
314     if (current_range_start_ < 0)
315       current_range_start_ = byte_range_.first_byte_position();
316   } else {
317     // This is not a range request but we have partial data stored.
318     current_range_start_ = 0;
319     byte_range_.set_last_byte_position(resource_size_ - 1);
320   }
321
322   bool rv = current_range_start_ >= 0;
323   if (!rv)
324     current_range_start_ = 0;
325
326   return rv;
327 }
328
329 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
330   if (headers->response_code() == 304) {
331     if (!byte_range_.IsValid() || truncated_)
332       return true;
333
334     // We must have a complete range here.
335     return byte_range_.HasFirstBytePosition() &&
336         byte_range_.HasLastBytePosition();
337   }
338
339   int64 start, end, total_length;
340   if (!headers->GetContentRange(&start, &end, &total_length))
341     return false;
342   if (total_length <= 0)
343     return false;
344
345   DCHECK_EQ(headers->response_code(), 206);
346
347   // A server should return a valid content length with a 206 (per the standard)
348   // but relax the requirement because some servers don't do that.
349   int64 content_length = headers->GetContentLength();
350   if (content_length > 0 && content_length != end - start + 1)
351     return false;
352
353   if (!resource_size_) {
354     // First response. Update our values with the ones provided by the server.
355     resource_size_ = total_length;
356     if (!byte_range_.HasFirstBytePosition()) {
357       byte_range_.set_first_byte_position(start);
358       current_range_start_ = start;
359     }
360     if (!byte_range_.HasLastBytePosition())
361       byte_range_.set_last_byte_position(end);
362   } else if (resource_size_ != total_length) {
363     return false;
364   }
365
366   if (truncated_) {
367     if (!byte_range_.HasLastBytePosition())
368       byte_range_.set_last_byte_position(end);
369   }
370
371   if (start != current_range_start_)
372     return false;
373
374   if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
375     return false;
376
377   return true;
378 }
379
380 // We are making multiple requests to complete the range requested by the user.
381 // Just assume that everything is fine and say that we are returning what was
382 // requested.
383 void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
384                                      bool success) {
385   if (truncated_)
386     return;
387
388   if (byte_range_.IsValid() && success) {
389     headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_);
390     return;
391   }
392
393   headers->RemoveHeader(kLengthHeader);
394   headers->RemoveHeader(kRangeHeader);
395
396   if (byte_range_.IsValid()) {
397     headers->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable");
398     headers->AddHeader(base::StringPrintf("%s: bytes 0-0/%" PRId64,
399                                           kRangeHeader, resource_size_));
400     headers->AddHeader(base::StringPrintf("%s: 0", kLengthHeader));
401   } else {
402     // TODO(rvargas): Is it safe to change the protocol version?
403     headers->ReplaceStatusLine("HTTP/1.1 200 OK");
404     DCHECK_NE(resource_size_, 0);
405     headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
406                                           resource_size_));
407   }
408 }
409
410 void PartialData::FixContentLength(HttpResponseHeaders* headers) {
411   headers->RemoveHeader(kLengthHeader);
412   headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
413                                         resource_size_));
414 }
415
416 int PartialData::CacheRead(
417     disk_cache::Entry* entry, IOBuffer* data, int data_len,
418     const net::CompletionCallback& callback) {
419   int read_len = std::min(data_len, cached_min_len_);
420   if (!read_len)
421     return 0;
422
423   int rv = 0;
424   if (sparse_entry_) {
425     rv = entry->ReadSparseData(current_range_start_, data, read_len,
426                                callback);
427   } else {
428     if (current_range_start_ > kint32max)
429       return ERR_INVALID_ARGUMENT;
430
431     rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
432                          data, read_len, callback);
433   }
434   return rv;
435 }
436
437 int PartialData::CacheWrite(
438     disk_cache::Entry* entry, IOBuffer* data, int data_len,
439     const net::CompletionCallback& callback) {
440   DVLOG(3) << "To write: " << data_len;
441   if (sparse_entry_) {
442     return entry->WriteSparseData(
443         current_range_start_, data, data_len, callback);
444   } else  {
445     if (current_range_start_ > kint32max)
446       return ERR_INVALID_ARGUMENT;
447
448     return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
449                             data, data_len, callback, true);
450   }
451 }
452
453 void PartialData::OnCacheReadCompleted(int result) {
454   DVLOG(3) << "Read: " << result;
455   if (result > 0) {
456     current_range_start_ += result;
457     cached_min_len_ -= result;
458     DCHECK_GE(cached_min_len_, 0);
459   }
460 }
461
462 void PartialData::OnNetworkReadCompleted(int result) {
463   if (result > 0)
464     current_range_start_ += result;
465 }
466
467 int PartialData::GetNextRangeLen() {
468   int64 range_len =
469       byte_range_.HasLastBytePosition() ?
470       byte_range_.last_byte_position() - current_range_start_ + 1 :
471       kint32max;
472   if (range_len > kint32max)
473     range_len = kint32max;
474   return static_cast<int32>(range_len);
475 }
476
477 void PartialData::GetAvailableRangeCompleted(int result, int64 start) {
478   DCHECK(!callback_.is_null());
479   DCHECK_NE(ERR_IO_PENDING, result);
480
481   cached_start_ = start;
482   cached_min_len_ = result;
483   if (result >= 0)
484     result = 1;  // Return success, go ahead and validate the entry.
485
486   CompletionCallback cb = callback_;
487   callback_.Reset();
488   cb.Run(result);
489 }
490
491 }  // namespace net