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