[go: nahoru, domu]

blob: 57985e721bd2bfa1950a9653a775ba2d66806b58 [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_);
rvargas@google.com28accfe2009-09-04 23:36:3368 } else if (truncated_) {
69 if (!current_range_start_) {
70 // Update the cached range only the first time.
71 cached_min_len_ = static_cast<int32>(byte_range_.first_byte_position());
72 cached_start_ = 0;
73 }
rvargas@google.come5dad132009-08-18 00:53:4174 } else {
75 cached_min_len_ = len;
76 cached_start_ = current_range_start_;
77 }
78
rvargas@google.com8bf26f49a2009-06-12 17:35:5079 if (cached_min_len_ < 0) {
80 DCHECK(cached_min_len_ != ERR_IO_PENDING);
81 return cached_min_len_;
82 }
83
84 headers->assign(extra_headers_);
85
86 if (!cached_min_len_) {
87 // We don't have anything else stored.
88 final_range_ = true;
rvargas@google.com28accfe2009-09-04 23:36:3389 cached_start_ =
90 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:5091 }
92
93 if (current_range_start_ == cached_start_) {
94 // The data lives in the cache.
95 range_present_ = true;
96 if (len == cached_min_len_)
97 final_range_ = true;
98 AddRangeHeader(current_range_start_, cached_start_ + cached_min_len_ - 1,
99 headers);
100 } else {
101 // This range is not in the cache.
102 AddRangeHeader(current_range_start_, cached_start_ - 1, headers);
103 }
104
105 // Return a positive number to indicate success (versus error or finished).
106 return 1;
107}
108
109bool PartialData::IsCurrentRangeCached() const {
110 return range_present_;
111}
112
113bool PartialData::IsLastRange() const {
114 return final_range_;
115}
116
rvargas@google.com44f873a62009-08-12 00:14:48117bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
rvargas@google.com28accfe2009-09-04 23:36:33118 disk_cache::Entry* entry,
119 bool truncated) {
rvargas@google.com67fe45c2009-06-24 17:44:57120 resource_size_ = 0;
rvargas@google.com28accfe2009-09-04 23:36:33121 if (truncated) {
122 DCHECK_EQ(headers->response_code(), 200);
rvargas@google.com28accfe2009-09-04 23:36:33123 // We don't have the real length and the user may be trying to create a
124 // sparse entry so let's not write to this entry.
125 if (byte_range_.IsValid())
126 return false;
127
hclam@chromium.orgecd8becb2009-10-02 17:57:45128 truncated_ = true;
129 sparse_entry_ = false;
rvargas@google.com28accfe2009-09-04 23:36:33130 byte_range_.set_first_byte_position(entry->GetDataSize(kDataStream));
131 current_range_start_ = 0;
132 return true;
133 }
134
rvargas@google.come5dad132009-08-18 00:53:41135 if (headers->response_code() == 200) {
136 DCHECK(byte_range_.IsValid());
137 sparse_entry_ = false;
138 resource_size_ = entry->GetDataSize(kDataStream);
139 return true;
140 }
141
rvargas@google.com28accfe2009-09-04 23:36:33142 std::string length_value;
rvargas@google.com67fe45c2009-06-24 17:44:57143 if (!headers->GetNormalizedHeader(kLengthHeader, &length_value))
144 return false; // We must have stored the resource length.
145
rvargas@google.com44f873a62009-08-12 00:14:48146 if (!StringToInt64(length_value, &resource_size_) || !resource_size_)
rvargas@google.com67fe45c2009-06-24 17:44:57147 return false;
148
rvargas@google.come5dad132009-08-18 00:53:41149 // Make sure that this is really a sparse entry.
150 int64 n;
151 if (ERR_CACHE_OPERATION_NOT_SUPPORTED == entry->GetAvailableRange(0, 5, &n))
152 return false;
153
154 return true;
155}
156
157bool PartialData::IsRequestedRangeOK() {
rvargas@google.com44f873a62009-08-12 00:14:48158 if (byte_range_.IsValid()) {
rvargas@google.com28accfe2009-09-04 23:36:33159 if (truncated_)
160 return true;
rvargas@google.com44f873a62009-08-12 00:14:48161 if (!byte_range_.ComputeBounds(resource_size_))
162 return false;
rvargas@google.com67fe45c2009-06-24 17:44:57163
rvargas@google.com44f873a62009-08-12 00:14:48164 if (current_range_start_ < 0)
165 current_range_start_ = byte_range_.first_byte_position();
166 } else {
167 // This is not a range request but we have partial data stored.
168 current_range_start_ = 0;
169 byte_range_.set_last_byte_position(resource_size_ - 1);
170 }
171
rvargas@google.come5dad132009-08-18 00:53:41172 bool rv = current_range_start_ >= 0;
173 if (!rv)
174 current_range_start_ = 0;
rvargas@google.com67fe45c2009-06-24 17:44:57175
rvargas@google.come5dad132009-08-18 00:53:41176 return rv;
rvargas@google.com95792eb12009-06-22 21:30:40177}
178
179bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
rvargas@google.come5dad132009-08-18 00:53:41180 if (headers->response_code() == 304) {
hclam@chromium.orgd9adff2c2009-09-05 01:15:45181 if (!byte_range_.IsValid() || truncated_)
rvargas@google.com28accfe2009-09-04 23:36:33182 return true;
183
rvargas@google.come5dad132009-08-18 00:53:41184 // We must have a complete range here.
185 return byte_range_.HasFirstBytePosition() &&
186 byte_range_.HasLastBytePosition();
187 }
188
rvargas@google.com95792eb12009-06-22 21:30:40189 int64 start, end, total_length;
190 if (!headers->GetContentRange(&start, &end, &total_length))
191 return false;
192 if (total_length <= 0)
193 return false;
194
rvargas@google.com7eab0d2262009-10-14 22:05:54195 int64 content_length = headers->GetContentLength();
196 if (content_length != end - start + 1)
197 return false;
198
rvargas@google.com95792eb12009-06-22 21:30:40199 if (!resource_size_) {
200 // First response. Update our values with the ones provided by the server.
201 resource_size_ = total_length;
rvargas@google.com67fe45c2009-06-24 17:44:57202 if (!byte_range_.HasFirstBytePosition()) {
rvargas@google.com95792eb12009-06-22 21:30:40203 byte_range_.set_first_byte_position(start);
rvargas@google.com67fe45c2009-06-24 17:44:57204 current_range_start_ = start;
205 }
rvargas@google.com95792eb12009-06-22 21:30:40206 if (!byte_range_.HasLastBytePosition())
207 byte_range_.set_last_byte_position(end);
208 } else if (resource_size_ != total_length) {
209 return false;
210 }
211
212 if (start != current_range_start_)
213 return false;
214
rvargas@google.com44f873a62009-08-12 00:14:48215 if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
rvargas@google.com95792eb12009-06-22 21:30:40216 return false;
217
218 return true;
219}
220
221// We are making multiple requests to complete the range requested by the user.
222// Just assume that everything is fine and say that we are returning what was
223// requested.
224void PartialData::FixResponseHeaders(HttpResponseHeaders* headers) {
rvargas@google.com28accfe2009-09-04 23:36:33225 if (truncated_)
226 return;
227
rvargas@google.com95792eb12009-06-22 21:30:40228 headers->RemoveHeader(kLengthHeader);
229 headers->RemoveHeader(kRangeHeader);
230
rvargas@google.com44f873a62009-08-12 00:14:48231 int64 range_len;
232 if (byte_range_.IsValid()) {
rvargas@google.come5dad132009-08-18 00:53:41233 if (!sparse_entry_)
234 headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
235
rvargas@google.com44f873a62009-08-12 00:14:48236 DCHECK(byte_range_.HasFirstBytePosition());
237 DCHECK(byte_range_.HasLastBytePosition());
238 headers->AddHeader(StringPrintf("%s: bytes %lld-%lld/%lld", kRangeHeader,
239 byte_range_.first_byte_position(),
240 byte_range_.last_byte_position(),
241 resource_size_));
242 range_len = byte_range_.last_byte_position() -
243 byte_range_.first_byte_position() + 1;
244 } else {
245 // TODO(rvargas): Is it safe to change the protocol version?
246 headers->ReplaceStatusLine("HTTP/1.1 200 OK");
247 DCHECK_NE(resource_size_, 0);
248 range_len = resource_size_;
249 }
rvargas@google.com95792eb12009-06-22 21:30:40250
rvargas@google.com95792eb12009-06-22 21:30:40251 headers->AddHeader(StringPrintf("%s: %lld", kLengthHeader, range_len));
252}
253
254void PartialData::FixContentLength(HttpResponseHeaders* headers) {
255 headers->RemoveHeader(kLengthHeader);
256 headers->AddHeader(StringPrintf("%s: %lld", kLengthHeader, resource_size_));
257}
258
rvargas@google.com8bf26f49a2009-06-12 17:35:50259int PartialData::CacheRead(disk_cache::Entry* entry, IOBuffer* data,
260 int data_len, CompletionCallback* callback) {
261 int read_len = std::min(data_len, cached_min_len_);
hclam@chromium.org8f28d632009-10-01 22:09:21262 if (!read_len)
263 return 0;
264
rvargas@google.come5dad132009-08-18 00:53:41265 int rv = 0;
266 if (sparse_entry_) {
267 rv = entry->ReadSparseData(current_range_start_, data, read_len,
268 callback);
269 } else {
270 if (current_range_start_ > kint32max)
271 return ERR_INVALID_ARGUMENT;
272
273 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
274 data, read_len, callback);
275 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50276 return rv;
277}
278
279int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
280 int data_len, CompletionCallback* callback) {
rvargas@google.come5dad132009-08-18 00:53:41281 if (sparse_entry_) {
282 return entry->WriteSparseData(current_range_start_, data, data_len,
283 callback);
284 } else {
285 if (current_range_start_ > kint32max)
286 return ERR_INVALID_ARGUMENT;
287
288 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
rvargas@google.com28accfe2009-09-04 23:36:33289 data, data_len, callback, true);
rvargas@google.come5dad132009-08-18 00:53:41290 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50291}
292
293void PartialData::OnCacheReadCompleted(int result) {
294 if (result > 0) {
295 current_range_start_ += result;
296 cached_min_len_ -= result;
297 DCHECK(cached_min_len_ >= 0);
rvargas@google.com8bf26f49a2009-06-12 17:35:50298 }
299}
300
301void PartialData::OnNetworkReadCompleted(int result) {
302 if (result > 0)
303 current_range_start_ += result;
304}
305
306// Static.
307void PartialData::AddRangeHeader(int64 start, int64 end, std::string* headers) {
rvargas@google.com67fe45c2009-06-24 17:44:57308 DCHECK(start >= 0 || end >= 0);
309 std::string my_start, my_end;
310 if (start >= 0)
311 my_start = Int64ToString(start);
312 if (end >= 0)
313 my_end = Int64ToString(end);
314
315 headers->append(StringPrintf("Range: bytes=%s-%s\r\n", my_start.c_str(),
316 my_end.c_str()));
rvargas@google.com8bf26f49a2009-06-12 17:35:50317}
rvargas@google.com8bf26f49a2009-06-12 17:35:50318
319} // namespace net