[go: nahoru, domu]

blob: d24d6fa9e248e10237c5bd935d59ee5f97d85163 [file] [log] [blame]
rvargas@google.com8bf26f49a2009-06-12 17:35:501// Copyright (c) 2009 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/logging.h"
8#include "base/string_util.h"
9#include "net/base/net_errors.h"
10#include "net/disk_cache/disk_cache.h"
rvargas@google.com95792eb12009-06-22 21:30:4011#include "net/http/http_response_headers.h"
rvargas@google.com8bf26f49a2009-06-12 17:35:5012#include "net/http/http_util.h"
rvargas@google.com95792eb12009-06-22 21:30:4013
14namespace {
15
16// The headers that we have to process.
17const char kLengthHeader[] = "Content-Length";
18const char kRangeHeader[] = "Content-Range";
rvargas@google.come5dad132009-08-18 00:53:4119const int kDataStream = 1;
rvargas@google.com95792eb12009-06-22 21:30:4020
21}
rvargas@google.com8bf26f49a2009-06-12 17:35:5022
23namespace net {
24
25bool PartialData::Init(const std::string& headers,
26 const std::string& new_headers) {
27 std::vector<HttpByteRange> ranges;
28 if (!HttpUtil::ParseRanges(headers, &ranges) || ranges.size() != 1)
29 return false;
30
31 // We can handle this range request.
32 byte_range_ = ranges[0];
33 if (!byte_range_.IsValid())
34 return false;
35
36 extra_headers_ = new_headers;
rvargas@google.com95792eb12009-06-22 21:30:4037 resource_size_ = 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:5038
rvargas@google.com8bf26f49a2009-06-12 17:35:5039 current_range_start_ = byte_range_.first_byte_position();
40 return true;
41}
42
43void PartialData::RestoreHeaders(std::string* headers) const {
rvargas@google.com67fe45c2009-06-24 17:44:5744 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
45 int64 end = byte_range_.IsSuffixByteRange() ?
46 byte_range_.suffix_length() : byte_range_.last_byte_position();
rvargas@google.com8bf26f49a2009-06-12 17:35:5047
rvargas@google.com67fe45c2009-06-24 17:44:5748 AddRangeHeader(current_range_start_, end, headers);
rvargas@google.com8bf26f49a2009-06-12 17:35:5049}
50
51int PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
52 std::string* headers) {
53 DCHECK(current_range_start_ >= 0);
54
55 // Scan the disk cache for the first cached portion within this range.
56 int64 range_len = byte_range_.HasLastBytePosition() ?
rvargas@google.com44f873a62009-08-12 00:14:4857 byte_range_.last_byte_position() - current_range_start_ + 1 : kint32max;
rvargas@google.com8bf26f49a2009-06-12 17:35:5058 if (range_len > kint32max)
59 range_len = kint32max;
60 int len = static_cast<int32>(range_len);
61 if (!len)
62 return 0;
63 range_present_ = false;
64
rvargas@google.come5dad132009-08-18 00:53:4165 if (sparse_entry_) {
66 cached_min_len_ = entry->GetAvailableRange(current_range_start_, len,
67 &cached_start_);
68 } else {
69 cached_min_len_ = len;
70 cached_start_ = current_range_start_;
71 }
72
rvargas@google.com8bf26f49a2009-06-12 17:35:5073 if (cached_min_len_ < 0) {
74 DCHECK(cached_min_len_ != ERR_IO_PENDING);
75 return cached_min_len_;
76 }
77
78 headers->assign(extra_headers_);
79
80 if (!cached_min_len_) {
81 // We don't have anything else stored.
82 final_range_ = true;
83 cached_start_ = current_range_start_ + len;
84 }
85
86 if (current_range_start_ == cached_start_) {
87 // The data lives in the cache.
88 range_present_ = true;
89 if (len == cached_min_len_)
90 final_range_ = true;
91 AddRangeHeader(current_range_start_, cached_start_ + cached_min_len_ - 1,
92 headers);
93 } else {
94 // This range is not in the cache.
95 AddRangeHeader(current_range_start_, cached_start_ - 1, headers);
96 }
97
98 // Return a positive number to indicate success (versus error or finished).
99 return 1;
100}
101
102bool PartialData::IsCurrentRangeCached() const {
103 return range_present_;
104}
105
106bool PartialData::IsLastRange() const {
107 return final_range_;
108}
109
rvargas@google.com44f873a62009-08-12 00:14:48110bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
111 disk_cache::Entry* entry) {
rvargas@google.com95792eb12009-06-22 21:30:40112 std::string length_value;
rvargas@google.com67fe45c2009-06-24 17:44:57113 resource_size_ = 0;
rvargas@google.come5dad132009-08-18 00:53:41114 if (headers->response_code() == 200) {
115 DCHECK(byte_range_.IsValid());
116 sparse_entry_ = false;
117 resource_size_ = entry->GetDataSize(kDataStream);
118 return true;
119 }
120
rvargas@google.com67fe45c2009-06-24 17:44:57121 if (!headers->GetNormalizedHeader(kLengthHeader, &length_value))
122 return false; // We must have stored the resource length.
123
rvargas@google.com44f873a62009-08-12 00:14:48124 if (!StringToInt64(length_value, &resource_size_) || !resource_size_)
rvargas@google.com67fe45c2009-06-24 17:44:57125 return false;
126
rvargas@google.come5dad132009-08-18 00:53:41127 // Make sure that this is really a sparse entry.
128 int64 n;
129 if (ERR_CACHE_OPERATION_NOT_SUPPORTED == entry->GetAvailableRange(0, 5, &n))
130 return false;
131
132 return true;
133}
134
135bool PartialData::IsRequestedRangeOK() {
rvargas@google.com44f873a62009-08-12 00:14:48136 if (byte_range_.IsValid()) {
137 if (!byte_range_.ComputeBounds(resource_size_))
138 return false;
rvargas@google.com67fe45c2009-06-24 17:44:57139
rvargas@google.com44f873a62009-08-12 00:14:48140 if (current_range_start_ < 0)
141 current_range_start_ = byte_range_.first_byte_position();
142 } else {
143 // This is not a range request but we have partial data stored.
144 current_range_start_ = 0;
145 byte_range_.set_last_byte_position(resource_size_ - 1);
146 }
147
rvargas@google.come5dad132009-08-18 00:53:41148 bool rv = current_range_start_ >= 0;
149 if (!rv)
150 current_range_start_ = 0;
rvargas@google.com67fe45c2009-06-24 17:44:57151
rvargas@google.come5dad132009-08-18 00:53:41152 return rv;
rvargas@google.com95792eb12009-06-22 21:30:40153}
154
155bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
rvargas@google.come5dad132009-08-18 00:53:41156 if (headers->response_code() == 304) {
157 // We must have a complete range here.
158 return byte_range_.HasFirstBytePosition() &&
159 byte_range_.HasLastBytePosition();
160 }
161
rvargas@google.com95792eb12009-06-22 21:30:40162 int64 start, end, total_length;
163 if (!headers->GetContentRange(&start, &end, &total_length))
164 return false;
165 if (total_length <= 0)
166 return false;
167
168 if (!resource_size_) {
169 // First response. Update our values with the ones provided by the server.
170 resource_size_ = total_length;
rvargas@google.com67fe45c2009-06-24 17:44:57171 if (!byte_range_.HasFirstBytePosition()) {
rvargas@google.com95792eb12009-06-22 21:30:40172 byte_range_.set_first_byte_position(start);
rvargas@google.com67fe45c2009-06-24 17:44:57173 current_range_start_ = start;
174 }
rvargas@google.com95792eb12009-06-22 21:30:40175 if (!byte_range_.HasLastBytePosition())
176 byte_range_.set_last_byte_position(end);
177 } else if (resource_size_ != total_length) {
178 return false;
179 }
180
181 if (start != current_range_start_)
182 return false;
183
rvargas@google.com44f873a62009-08-12 00:14:48184 if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
rvargas@google.com95792eb12009-06-22 21:30:40185 return false;
186
187 return true;
188}
189
190// We are making multiple requests to complete the range requested by the user.
191// Just assume that everything is fine and say that we are returning what was
192// requested.
193void PartialData::FixResponseHeaders(HttpResponseHeaders* headers) {
194 headers->RemoveHeader(kLengthHeader);
195 headers->RemoveHeader(kRangeHeader);
196
rvargas@google.com44f873a62009-08-12 00:14:48197 int64 range_len;
198 if (byte_range_.IsValid()) {
rvargas@google.come5dad132009-08-18 00:53:41199 if (!sparse_entry_)
200 headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
201
rvargas@google.com44f873a62009-08-12 00:14:48202 DCHECK(byte_range_.HasFirstBytePosition());
203 DCHECK(byte_range_.HasLastBytePosition());
204 headers->AddHeader(StringPrintf("%s: bytes %lld-%lld/%lld", kRangeHeader,
205 byte_range_.first_byte_position(),
206 byte_range_.last_byte_position(),
207 resource_size_));
208 range_len = byte_range_.last_byte_position() -
209 byte_range_.first_byte_position() + 1;
210 } else {
211 // TODO(rvargas): Is it safe to change the protocol version?
212 headers->ReplaceStatusLine("HTTP/1.1 200 OK");
213 DCHECK_NE(resource_size_, 0);
214 range_len = resource_size_;
215 }
rvargas@google.com95792eb12009-06-22 21:30:40216
rvargas@google.com95792eb12009-06-22 21:30:40217 headers->AddHeader(StringPrintf("%s: %lld", kLengthHeader, range_len));
218}
219
220void PartialData::FixContentLength(HttpResponseHeaders* headers) {
221 headers->RemoveHeader(kLengthHeader);
222 headers->AddHeader(StringPrintf("%s: %lld", kLengthHeader, resource_size_));
223}
224
rvargas@google.com8bf26f49a2009-06-12 17:35:50225int PartialData::CacheRead(disk_cache::Entry* entry, IOBuffer* data,
226 int data_len, CompletionCallback* callback) {
227 int read_len = std::min(data_len, cached_min_len_);
rvargas@google.come5dad132009-08-18 00:53:41228 int rv = 0;
229 if (sparse_entry_) {
230 rv = entry->ReadSparseData(current_range_start_, data, read_len,
231 callback);
232 } else {
233 if (current_range_start_ > kint32max)
234 return ERR_INVALID_ARGUMENT;
235
236 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
237 data, read_len, callback);
238 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50239 return rv;
240}
241
242int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
243 int data_len, CompletionCallback* callback) {
rvargas@google.come5dad132009-08-18 00:53:41244 if (sparse_entry_) {
245 return entry->WriteSparseData(current_range_start_, data, data_len,
246 callback);
247 } else {
248 if (current_range_start_ > kint32max)
249 return ERR_INVALID_ARGUMENT;
250
251 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
252 data, data_len, callback, false);
253 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50254}
255
256void PartialData::OnCacheReadCompleted(int result) {
257 if (result > 0) {
258 current_range_start_ += result;
259 cached_min_len_ -= result;
260 DCHECK(cached_min_len_ >= 0);
261 } else if (!result) {
262 // TODO(rvargas): we can detect this error and make sure that we are not
263 // in a loop of failure/retry.
264 }
265}
266
267void PartialData::OnNetworkReadCompleted(int result) {
268 if (result > 0)
269 current_range_start_ += result;
270}
271
272// Static.
273void PartialData::AddRangeHeader(int64 start, int64 end, std::string* headers) {
rvargas@google.com67fe45c2009-06-24 17:44:57274 DCHECK(start >= 0 || end >= 0);
275 std::string my_start, my_end;
276 if (start >= 0)
277 my_start = Int64ToString(start);
278 if (end >= 0)
279 my_end = Int64ToString(end);
280
281 headers->append(StringPrintf("Range: bytes=%s-%s\r\n", my_start.c_str(),
282 my_end.c_str()));
rvargas@google.com8bf26f49a2009-06-12 17:35:50283}
rvargas@google.com8bf26f49a2009-06-12 17:35:50284
285} // namespace net