[go: nahoru, domu]

blob: f7276997a829f980144009ebab041d6a46393973 [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
evan@chromium.org34b2b002009-11-20 06:53:287#include "base/format_macros.h"
rvargas@google.com8bf26f49a2009-06-12 17:35:508#include "base/logging.h"
9#include "base/string_util.h"
10#include "net/base/net_errors.h"
11#include "net/disk_cache/disk_cache.h"
rvargas@google.com95792eb12009-06-22 21:30:4012#include "net/http/http_response_headers.h"
rvargas@google.com8bf26f49a2009-06-12 17:35:5013#include "net/http/http_util.h"
rvargas@google.com95792eb12009-06-22 21:30:4014
15namespace {
16
17// The headers that we have to process.
18const char kLengthHeader[] = "Content-Length";
19const char kRangeHeader[] = "Content-Range";
rvargas@google.come5dad132009-08-18 00:53:4120const int kDataStream = 1;
rvargas@google.com95792eb12009-06-22 21:30:4021
22}
rvargas@google.com8bf26f49a2009-06-12 17:35:5023
24namespace net {
25
rvargas@google.come75e8af2009-11-03 00:04:2026bool PartialData::Init(const std::string& headers) {
rvargas@google.com8bf26f49a2009-06-12 17:35:5027 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
rvargas@google.com95792eb12009-06-22 21:30:4036 resource_size_ = 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:5037 current_range_start_ = byte_range_.first_byte_position();
38 return true;
39}
40
rvargas@google.come75e8af2009-11-03 00:04:2041void PartialData::SetHeaders(const std::string& headers) {
42 DCHECK(extra_headers_.empty());
43 extra_headers_ = headers;
44}
45
rvargas@google.com8bf26f49a2009-06-12 17:35:5046void PartialData::RestoreHeaders(std::string* headers) const {
rvargas@google.com67fe45c2009-06-24 17:44:5747 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
48 int64 end = byte_range_.IsSuffixByteRange() ?
49 byte_range_.suffix_length() : byte_range_.last_byte_position();
rvargas@google.com8bf26f49a2009-06-12 17:35:5050
rvargas@google.coma189bce2009-12-01 01:59:1251 headers->assign(extra_headers_);
52 if (byte_range_.IsValid())
53 AddRangeHeader(current_range_start_, end, headers);
rvargas@google.com8bf26f49a2009-06-12 17:35:5054}
55
56int PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
57 std::string* headers) {
58 DCHECK(current_range_start_ >= 0);
59
60 // Scan the disk cache for the first cached portion within this range.
61 int64 range_len = byte_range_.HasLastBytePosition() ?
rvargas@google.com44f873a62009-08-12 00:14:4862 byte_range_.last_byte_position() - current_range_start_ + 1 : kint32max;
rvargas@google.com8bf26f49a2009-06-12 17:35:5063 if (range_len > kint32max)
64 range_len = kint32max;
65 int len = static_cast<int32>(range_len);
66 if (!len)
67 return 0;
68 range_present_ = false;
69
rvargas@google.come5dad132009-08-18 00:53:4170 if (sparse_entry_) {
71 cached_min_len_ = entry->GetAvailableRange(current_range_start_, len,
72 &cached_start_);
rvargas@google.com28accfe2009-09-04 23:36:3373 } else if (truncated_) {
74 if (!current_range_start_) {
75 // Update the cached range only the first time.
76 cached_min_len_ = static_cast<int32>(byte_range_.first_byte_position());
77 cached_start_ = 0;
78 }
rvargas@google.come5dad132009-08-18 00:53:4179 } else {
80 cached_min_len_ = len;
81 cached_start_ = current_range_start_;
82 }
83
rvargas@google.com8bf26f49a2009-06-12 17:35:5084 if (cached_min_len_ < 0) {
85 DCHECK(cached_min_len_ != ERR_IO_PENDING);
86 return cached_min_len_;
87 }
88
89 headers->assign(extra_headers_);
90
91 if (!cached_min_len_) {
92 // We don't have anything else stored.
93 final_range_ = true;
rvargas@google.com28accfe2009-09-04 23:36:3394 cached_start_ =
95 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:5096 }
97
98 if (current_range_start_ == cached_start_) {
99 // The data lives in the cache.
100 range_present_ = true;
101 if (len == cached_min_len_)
102 final_range_ = true;
103 AddRangeHeader(current_range_start_, cached_start_ + cached_min_len_ - 1,
104 headers);
105 } else {
106 // This range is not in the cache.
107 AddRangeHeader(current_range_start_, cached_start_ - 1, headers);
108 }
109
110 // Return a positive number to indicate success (versus error or finished).
111 return 1;
112}
113
114bool PartialData::IsCurrentRangeCached() const {
115 return range_present_;
116}
117
118bool PartialData::IsLastRange() const {
119 return final_range_;
120}
121
rvargas@google.com44f873a62009-08-12 00:14:48122bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
rvargas@google.com28accfe2009-09-04 23:36:33123 disk_cache::Entry* entry,
124 bool truncated) {
rvargas@google.com67fe45c2009-06-24 17:44:57125 resource_size_ = 0;
rvargas@google.com28accfe2009-09-04 23:36:33126 if (truncated) {
127 DCHECK_EQ(headers->response_code(), 200);
rvargas@google.com28accfe2009-09-04 23:36:33128 // We don't have the real length and the user may be trying to create a
129 // sparse entry so let's not write to this entry.
130 if (byte_range_.IsValid())
131 return false;
132
rvargas@google.comdbd39fb2010-01-08 01:13:36133 // Now we avoid resume if there is no content length, but that was not
134 // always the case so double check here.
135 int64 total_length = headers->GetContentLength();
136 if (total_length <= 0 || !headers->HasStrongValidators())
137 return false;
138
hclam@chromium.orgecd8becb2009-10-02 17:57:45139 truncated_ = true;
140 sparse_entry_ = false;
rvargas@google.com28accfe2009-09-04 23:36:33141 byte_range_.set_first_byte_position(entry->GetDataSize(kDataStream));
rvargas@google.comdbd39fb2010-01-08 01:13:36142 resource_size_ = total_length;
rvargas@google.com28accfe2009-09-04 23:36:33143 current_range_start_ = 0;
144 return true;
145 }
146
rvargas@google.come5dad132009-08-18 00:53:41147 if (headers->response_code() == 200) {
148 DCHECK(byte_range_.IsValid());
149 sparse_entry_ = false;
150 resource_size_ = entry->GetDataSize(kDataStream);
151 return true;
152 }
153
rvargas@google.comdbd39fb2010-01-08 01:13:36154 int64 length_value = headers->GetContentLength();
155 if (length_value <= 0)
rvargas@google.com67fe45c2009-06-24 17:44:57156 return false; // We must have stored the resource length.
157
rvargas@google.comdbd39fb2010-01-08 01:13:36158 resource_size_ = length_value;
rvargas@google.com67fe45c2009-06-24 17:44:57159
rvargas@google.come5dad132009-08-18 00:53:41160 // Make sure that this is really a sparse entry.
161 int64 n;
162 if (ERR_CACHE_OPERATION_NOT_SUPPORTED == entry->GetAvailableRange(0, 5, &n))
163 return false;
164
165 return true;
166}
167
168bool PartialData::IsRequestedRangeOK() {
rvargas@google.com44f873a62009-08-12 00:14:48169 if (byte_range_.IsValid()) {
rvargas@google.com28accfe2009-09-04 23:36:33170 if (truncated_)
171 return true;
rvargas@google.com44f873a62009-08-12 00:14:48172 if (!byte_range_.ComputeBounds(resource_size_))
173 return false;
rvargas@google.com67fe45c2009-06-24 17:44:57174
rvargas@google.com44f873a62009-08-12 00:14:48175 if (current_range_start_ < 0)
176 current_range_start_ = byte_range_.first_byte_position();
177 } else {
178 // This is not a range request but we have partial data stored.
179 current_range_start_ = 0;
180 byte_range_.set_last_byte_position(resource_size_ - 1);
181 }
182
rvargas@google.come5dad132009-08-18 00:53:41183 bool rv = current_range_start_ >= 0;
184 if (!rv)
185 current_range_start_ = 0;
rvargas@google.com67fe45c2009-06-24 17:44:57186
rvargas@google.come5dad132009-08-18 00:53:41187 return rv;
rvargas@google.com95792eb12009-06-22 21:30:40188}
189
190bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
rvargas@google.come5dad132009-08-18 00:53:41191 if (headers->response_code() == 304) {
hclam@chromium.orgd9adff2c2009-09-05 01:15:45192 if (!byte_range_.IsValid() || truncated_)
rvargas@google.com28accfe2009-09-04 23:36:33193 return true;
194
rvargas@google.come5dad132009-08-18 00:53:41195 // We must have a complete range here.
196 return byte_range_.HasFirstBytePosition() &&
197 byte_range_.HasLastBytePosition();
198 }
199
rvargas@google.com95792eb12009-06-22 21:30:40200 int64 start, end, total_length;
201 if (!headers->GetContentRange(&start, &end, &total_length))
202 return false;
203 if (total_length <= 0)
204 return false;
205
rvargas@google.com7eab0d2262009-10-14 22:05:54206 int64 content_length = headers->GetContentLength();
cevans@chromium.orgc3695f52009-10-28 18:29:54207 if (content_length < 0 || content_length != end - start + 1)
rvargas@google.com7eab0d2262009-10-14 22:05:54208 return false;
209
rvargas@google.com95792eb12009-06-22 21:30:40210 if (!resource_size_) {
211 // First response. Update our values with the ones provided by the server.
212 resource_size_ = total_length;
rvargas@google.com67fe45c2009-06-24 17:44:57213 if (!byte_range_.HasFirstBytePosition()) {
rvargas@google.com95792eb12009-06-22 21:30:40214 byte_range_.set_first_byte_position(start);
rvargas@google.com67fe45c2009-06-24 17:44:57215 current_range_start_ = start;
216 }
rvargas@google.com95792eb12009-06-22 21:30:40217 if (!byte_range_.HasLastBytePosition())
218 byte_range_.set_last_byte_position(end);
219 } else if (resource_size_ != total_length) {
220 return false;
221 }
222
rvargas@google.comdbd39fb2010-01-08 01:13:36223 if (truncated_) {
224 if (!byte_range_.HasLastBytePosition())
225 byte_range_.set_last_byte_position(end);
226 }
227
rvargas@google.com95792eb12009-06-22 21:30:40228 if (start != current_range_start_)
229 return false;
230
rvargas@google.com44f873a62009-08-12 00:14:48231 if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
rvargas@google.com95792eb12009-06-22 21:30:40232 return false;
233
234 return true;
235}
236
237// We are making multiple requests to complete the range requested by the user.
238// Just assume that everything is fine and say that we are returning what was
239// requested.
240void PartialData::FixResponseHeaders(HttpResponseHeaders* headers) {
rvargas@google.com28accfe2009-09-04 23:36:33241 if (truncated_)
242 return;
243
rvargas@google.com95792eb12009-06-22 21:30:40244 headers->RemoveHeader(kLengthHeader);
245 headers->RemoveHeader(kRangeHeader);
246
rvargas@google.com44f873a62009-08-12 00:14:48247 int64 range_len;
248 if (byte_range_.IsValid()) {
rvargas@google.come5dad132009-08-18 00:53:41249 if (!sparse_entry_)
250 headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
251
rvargas@google.com44f873a62009-08-12 00:14:48252 DCHECK(byte_range_.HasFirstBytePosition());
253 DCHECK(byte_range_.HasLastBytePosition());
evan@chromium.org34b2b002009-11-20 06:53:28254 headers->AddHeader(
255 StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
256 kRangeHeader,
257 byte_range_.first_byte_position(),
258 byte_range_.last_byte_position(),
259 resource_size_));
rvargas@google.com44f873a62009-08-12 00:14:48260 range_len = byte_range_.last_byte_position() -
261 byte_range_.first_byte_position() + 1;
262 } else {
263 // TODO(rvargas): Is it safe to change the protocol version?
264 headers->ReplaceStatusLine("HTTP/1.1 200 OK");
265 DCHECK_NE(resource_size_, 0);
266 range_len = resource_size_;
267 }
rvargas@google.com95792eb12009-06-22 21:30:40268
evan@chromium.org34b2b002009-11-20 06:53:28269 headers->AddHeader(StringPrintf("%s: %" PRId64, kLengthHeader, range_len));
rvargas@google.com95792eb12009-06-22 21:30:40270}
271
272void PartialData::FixContentLength(HttpResponseHeaders* headers) {
273 headers->RemoveHeader(kLengthHeader);
evan@chromium.org34b2b002009-11-20 06:53:28274 headers->AddHeader(StringPrintf("%s: %" PRId64, kLengthHeader,
275 resource_size_));
rvargas@google.com95792eb12009-06-22 21:30:40276}
277
rvargas@google.com8bf26f49a2009-06-12 17:35:50278int PartialData::CacheRead(disk_cache::Entry* entry, IOBuffer* data,
279 int data_len, CompletionCallback* callback) {
280 int read_len = std::min(data_len, cached_min_len_);
hclam@chromium.org8f28d632009-10-01 22:09:21281 if (!read_len)
282 return 0;
283
rvargas@google.come5dad132009-08-18 00:53:41284 int rv = 0;
285 if (sparse_entry_) {
286 rv = entry->ReadSparseData(current_range_start_, data, read_len,
287 callback);
288 } else {
289 if (current_range_start_ > kint32max)
290 return ERR_INVALID_ARGUMENT;
291
292 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
293 data, read_len, callback);
294 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50295 return rv;
296}
297
298int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
299 int data_len, CompletionCallback* callback) {
rvargas@google.come5dad132009-08-18 00:53:41300 if (sparse_entry_) {
301 return entry->WriteSparseData(current_range_start_, data, data_len,
302 callback);
303 } else {
304 if (current_range_start_ > kint32max)
305 return ERR_INVALID_ARGUMENT;
306
307 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
rvargas@google.com28accfe2009-09-04 23:36:33308 data, data_len, callback, true);
rvargas@google.come5dad132009-08-18 00:53:41309 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50310}
311
312void PartialData::OnCacheReadCompleted(int result) {
313 if (result > 0) {
314 current_range_start_ += result;
315 cached_min_len_ -= result;
316 DCHECK(cached_min_len_ >= 0);
rvargas@google.com8bf26f49a2009-06-12 17:35:50317 }
318}
319
320void PartialData::OnNetworkReadCompleted(int result) {
321 if (result > 0)
322 current_range_start_ += result;
323}
324
325// Static.
326void PartialData::AddRangeHeader(int64 start, int64 end, std::string* headers) {
rvargas@google.com67fe45c2009-06-24 17:44:57327 DCHECK(start >= 0 || end >= 0);
328 std::string my_start, my_end;
329 if (start >= 0)
330 my_start = Int64ToString(start);
331 if (end >= 0)
332 my_end = Int64ToString(end);
333
334 headers->append(StringPrintf("Range: bytes=%s-%s\r\n", my_start.c_str(),
335 my_end.c_str()));
rvargas@google.com8bf26f49a2009-06-12 17:35:50336}
rvargas@google.com8bf26f49a2009-06-12 17:35:50337
338} // namespace net