[go: nahoru, domu]

blob: f253dc1bd9dc4168fa5d85105c4e40c4fa2932ef [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.com67fe45c2009-06-24 17:44:5751 AddRangeHeader(current_range_start_, end, headers);
rvargas@google.com8bf26f49a2009-06-12 17:35:5052}
53
54int PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
55 std::string* headers) {
56 DCHECK(current_range_start_ >= 0);
57
58 // Scan the disk cache for the first cached portion within this range.
59 int64 range_len = byte_range_.HasLastBytePosition() ?
rvargas@google.com44f873a62009-08-12 00:14:4860 byte_range_.last_byte_position() - current_range_start_ + 1 : kint32max;
rvargas@google.com8bf26f49a2009-06-12 17:35:5061 if (range_len > kint32max)
62 range_len = kint32max;
63 int len = static_cast<int32>(range_len);
64 if (!len)
65 return 0;
66 range_present_ = false;
67
rvargas@google.come5dad132009-08-18 00:53:4168 if (sparse_entry_) {
69 cached_min_len_ = entry->GetAvailableRange(current_range_start_, len,
70 &cached_start_);
rvargas@google.com28accfe2009-09-04 23:36:3371 } else if (truncated_) {
72 if (!current_range_start_) {
73 // Update the cached range only the first time.
74 cached_min_len_ = static_cast<int32>(byte_range_.first_byte_position());
75 cached_start_ = 0;
76 }
rvargas@google.come5dad132009-08-18 00:53:4177 } else {
78 cached_min_len_ = len;
79 cached_start_ = current_range_start_;
80 }
81
rvargas@google.com8bf26f49a2009-06-12 17:35:5082 if (cached_min_len_ < 0) {
83 DCHECK(cached_min_len_ != ERR_IO_PENDING);
84 return cached_min_len_;
85 }
86
87 headers->assign(extra_headers_);
88
89 if (!cached_min_len_) {
90 // We don't have anything else stored.
91 final_range_ = true;
rvargas@google.com28accfe2009-09-04 23:36:3392 cached_start_ =
93 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:5094 }
95
96 if (current_range_start_ == cached_start_) {
97 // The data lives in the cache.
98 range_present_ = true;
99 if (len == cached_min_len_)
100 final_range_ = true;
101 AddRangeHeader(current_range_start_, cached_start_ + cached_min_len_ - 1,
102 headers);
103 } else {
104 // This range is not in the cache.
105 AddRangeHeader(current_range_start_, cached_start_ - 1, headers);
106 }
107
108 // Return a positive number to indicate success (versus error or finished).
109 return 1;
110}
111
112bool PartialData::IsCurrentRangeCached() const {
113 return range_present_;
114}
115
116bool PartialData::IsLastRange() const {
117 return final_range_;
118}
119
rvargas@google.com44f873a62009-08-12 00:14:48120bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
rvargas@google.com28accfe2009-09-04 23:36:33121 disk_cache::Entry* entry,
122 bool truncated) {
rvargas@google.com67fe45c2009-06-24 17:44:57123 resource_size_ = 0;
rvargas@google.com28accfe2009-09-04 23:36:33124 if (truncated) {
125 DCHECK_EQ(headers->response_code(), 200);
rvargas@google.com28accfe2009-09-04 23:36:33126 // We don't have the real length and the user may be trying to create a
127 // sparse entry so let's not write to this entry.
128 if (byte_range_.IsValid())
129 return false;
130
hclam@chromium.orgecd8becb2009-10-02 17:57:45131 truncated_ = true;
132 sparse_entry_ = false;
rvargas@google.com28accfe2009-09-04 23:36:33133 byte_range_.set_first_byte_position(entry->GetDataSize(kDataStream));
134 current_range_start_ = 0;
135 return true;
136 }
137
rvargas@google.come5dad132009-08-18 00:53:41138 if (headers->response_code() == 200) {
139 DCHECK(byte_range_.IsValid());
140 sparse_entry_ = false;
141 resource_size_ = entry->GetDataSize(kDataStream);
142 return true;
143 }
144
rvargas@google.com28accfe2009-09-04 23:36:33145 std::string length_value;
rvargas@google.com67fe45c2009-06-24 17:44:57146 if (!headers->GetNormalizedHeader(kLengthHeader, &length_value))
147 return false; // We must have stored the resource length.
148
rvargas@google.com44f873a62009-08-12 00:14:48149 if (!StringToInt64(length_value, &resource_size_) || !resource_size_)
rvargas@google.com67fe45c2009-06-24 17:44:57150 return false;
151
rvargas@google.come5dad132009-08-18 00:53:41152 // Make sure that this is really a sparse entry.
153 int64 n;
154 if (ERR_CACHE_OPERATION_NOT_SUPPORTED == entry->GetAvailableRange(0, 5, &n))
155 return false;
156
157 return true;
158}
159
160bool PartialData::IsRequestedRangeOK() {
rvargas@google.com44f873a62009-08-12 00:14:48161 if (byte_range_.IsValid()) {
rvargas@google.com28accfe2009-09-04 23:36:33162 if (truncated_)
163 return true;
rvargas@google.com44f873a62009-08-12 00:14:48164 if (!byte_range_.ComputeBounds(resource_size_))
165 return false;
rvargas@google.com67fe45c2009-06-24 17:44:57166
rvargas@google.com44f873a62009-08-12 00:14:48167 if (current_range_start_ < 0)
168 current_range_start_ = byte_range_.first_byte_position();
169 } else {
170 // This is not a range request but we have partial data stored.
171 current_range_start_ = 0;
172 byte_range_.set_last_byte_position(resource_size_ - 1);
173 }
174
rvargas@google.come5dad132009-08-18 00:53:41175 bool rv = current_range_start_ >= 0;
176 if (!rv)
177 current_range_start_ = 0;
rvargas@google.com67fe45c2009-06-24 17:44:57178
rvargas@google.come5dad132009-08-18 00:53:41179 return rv;
rvargas@google.com95792eb12009-06-22 21:30:40180}
181
182bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
rvargas@google.come5dad132009-08-18 00:53:41183 if (headers->response_code() == 304) {
hclam@chromium.orgd9adff2c2009-09-05 01:15:45184 if (!byte_range_.IsValid() || truncated_)
rvargas@google.com28accfe2009-09-04 23:36:33185 return true;
186
rvargas@google.come5dad132009-08-18 00:53:41187 // We must have a complete range here.
188 return byte_range_.HasFirstBytePosition() &&
189 byte_range_.HasLastBytePosition();
190 }
191
rvargas@google.com95792eb12009-06-22 21:30:40192 int64 start, end, total_length;
193 if (!headers->GetContentRange(&start, &end, &total_length))
194 return false;
195 if (total_length <= 0)
196 return false;
197
rvargas@google.com7eab0d2262009-10-14 22:05:54198 int64 content_length = headers->GetContentLength();
cevans@chromium.orgc3695f52009-10-28 18:29:54199 if (content_length < 0 || content_length != end - start + 1)
rvargas@google.com7eab0d2262009-10-14 22:05:54200 return false;
201
rvargas@google.com95792eb12009-06-22 21:30:40202 if (!resource_size_) {
203 // First response. Update our values with the ones provided by the server.
204 resource_size_ = total_length;
rvargas@google.com67fe45c2009-06-24 17:44:57205 if (!byte_range_.HasFirstBytePosition()) {
rvargas@google.com95792eb12009-06-22 21:30:40206 byte_range_.set_first_byte_position(start);
rvargas@google.com67fe45c2009-06-24 17:44:57207 current_range_start_ = start;
208 }
rvargas@google.com95792eb12009-06-22 21:30:40209 if (!byte_range_.HasLastBytePosition())
210 byte_range_.set_last_byte_position(end);
211 } else if (resource_size_ != total_length) {
212 return false;
213 }
214
215 if (start != current_range_start_)
216 return false;
217
rvargas@google.com44f873a62009-08-12 00:14:48218 if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
rvargas@google.com95792eb12009-06-22 21:30:40219 return false;
220
221 return true;
222}
223
224// We are making multiple requests to complete the range requested by the user.
225// Just assume that everything is fine and say that we are returning what was
226// requested.
227void PartialData::FixResponseHeaders(HttpResponseHeaders* headers) {
rvargas@google.com28accfe2009-09-04 23:36:33228 if (truncated_)
229 return;
230
rvargas@google.com95792eb12009-06-22 21:30:40231 headers->RemoveHeader(kLengthHeader);
232 headers->RemoveHeader(kRangeHeader);
233
rvargas@google.com44f873a62009-08-12 00:14:48234 int64 range_len;
235 if (byte_range_.IsValid()) {
rvargas@google.come5dad132009-08-18 00:53:41236 if (!sparse_entry_)
237 headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
238
rvargas@google.com44f873a62009-08-12 00:14:48239 DCHECK(byte_range_.HasFirstBytePosition());
240 DCHECK(byte_range_.HasLastBytePosition());
evan@chromium.org34b2b002009-11-20 06:53:28241 headers->AddHeader(
242 StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
243 kRangeHeader,
244 byte_range_.first_byte_position(),
245 byte_range_.last_byte_position(),
246 resource_size_));
rvargas@google.com44f873a62009-08-12 00:14:48247 range_len = byte_range_.last_byte_position() -
248 byte_range_.first_byte_position() + 1;
249 } else {
250 // TODO(rvargas): Is it safe to change the protocol version?
251 headers->ReplaceStatusLine("HTTP/1.1 200 OK");
252 DCHECK_NE(resource_size_, 0);
253 range_len = resource_size_;
254 }
rvargas@google.com95792eb12009-06-22 21:30:40255
evan@chromium.org34b2b002009-11-20 06:53:28256 headers->AddHeader(StringPrintf("%s: %" PRId64, kLengthHeader, range_len));
rvargas@google.com95792eb12009-06-22 21:30:40257}
258
259void PartialData::FixContentLength(HttpResponseHeaders* headers) {
260 headers->RemoveHeader(kLengthHeader);
evan@chromium.org34b2b002009-11-20 06:53:28261 headers->AddHeader(StringPrintf("%s: %" PRId64, kLengthHeader,
262 resource_size_));
rvargas@google.com95792eb12009-06-22 21:30:40263}
264
rvargas@google.com8bf26f49a2009-06-12 17:35:50265int PartialData::CacheRead(disk_cache::Entry* entry, IOBuffer* data,
266 int data_len, CompletionCallback* callback) {
267 int read_len = std::min(data_len, cached_min_len_);
hclam@chromium.org8f28d632009-10-01 22:09:21268 if (!read_len)
269 return 0;
270
rvargas@google.come5dad132009-08-18 00:53:41271 int rv = 0;
272 if (sparse_entry_) {
273 rv = entry->ReadSparseData(current_range_start_, data, read_len,
274 callback);
275 } else {
276 if (current_range_start_ > kint32max)
277 return ERR_INVALID_ARGUMENT;
278
279 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
280 data, read_len, callback);
281 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50282 return rv;
283}
284
285int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
286 int data_len, CompletionCallback* callback) {
rvargas@google.come5dad132009-08-18 00:53:41287 if (sparse_entry_) {
288 return entry->WriteSparseData(current_range_start_, data, data_len,
289 callback);
290 } else {
291 if (current_range_start_ > kint32max)
292 return ERR_INVALID_ARGUMENT;
293
294 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
rvargas@google.com28accfe2009-09-04 23:36:33295 data, data_len, callback, true);
rvargas@google.come5dad132009-08-18 00:53:41296 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50297}
298
299void PartialData::OnCacheReadCompleted(int result) {
300 if (result > 0) {
301 current_range_start_ += result;
302 cached_min_len_ -= result;
303 DCHECK(cached_min_len_ >= 0);
rvargas@google.com8bf26f49a2009-06-12 17:35:50304 }
305}
306
307void PartialData::OnNetworkReadCompleted(int result) {
308 if (result > 0)
309 current_range_start_ += result;
310}
311
312// Static.
313void PartialData::AddRangeHeader(int64 start, int64 end, std::string* headers) {
rvargas@google.com67fe45c2009-06-24 17:44:57314 DCHECK(start >= 0 || end >= 0);
315 std::string my_start, my_end;
316 if (start >= 0)
317 my_start = Int64ToString(start);
318 if (end >= 0)
319 my_end = Int64ToString(end);
320
321 headers->append(StringPrintf("Range: bytes=%s-%s\r\n", my_start.c_str(),
322 my_end.c_str()));
rvargas@google.com8bf26f49a2009-06-12 17:35:50323}
rvargas@google.com8bf26f49a2009-06-12 17:35:50324
325} // namespace net