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 "google_apis/drive/base_requests.h"
6
7#include "base/json/json_reader.h"
8#include "base/location.h"
9#include "base/sequenced_task_runner.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/stringprintf.h"
12#include "base/task_runner_util.h"
13#include "base/values.h"
14#include "google_apis/drive/request_sender.h"
15#include "google_apis/drive/task_util.h"
16#include "net/base/io_buffer.h"
17#include "net/base/load_flags.h"
18#include "net/base/net_errors.h"
19#include "net/http/http_byte_range.h"
20#include "net/http/http_response_headers.h"
21#include "net/http/http_util.h"
22#include "net/url_request/url_fetcher.h"
23#include "net/url_request/url_request_status.h"
24
25using net::URLFetcher;
26
27namespace {
28
29// Template for optional OAuth2 authorization HTTP header.
30const char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s";
31// Template for GData API version HTTP header.
32const char kGDataVersionHeader[] = "GData-Version: 3.0";
33
34// Maximum number of attempts for re-authentication per request.
35const int kMaxReAuthenticateAttemptsPerRequest = 1;
36
37// Template for initiate upload of both GData WAPI and Drive API v2.
38const char kUploadContentType[] = "X-Upload-Content-Type: ";
39const char kUploadContentLength[] = "X-Upload-Content-Length: ";
40const char kUploadResponseLocation[] = "location";
41
42// Template for upload data range of both GData WAPI and Drive API v2.
43const char kUploadContentRange[] = "Content-Range: bytes ";
44const char kUploadResponseRange[] = "range";
45
46// Parses JSON passed in |json| on |blocking_task_runner|. Runs |callback| on
47// the calling thread when finished with either success or failure.
48// The callback must not be null.
49void ParseJsonOnBlockingPool(
50    base::TaskRunner* blocking_task_runner,
51    const std::string& json,
52    const base::Callback<void(scoped_ptr<base::Value> value)>& callback) {
53  base::PostTaskAndReplyWithResult(
54      blocking_task_runner,
55      FROM_HERE,
56      base::Bind(&google_apis::ParseJson, json),
57      callback);
58}
59
60// Returns response headers as a string. Returns a warning message if
61// |url_fetcher| does not contain a valid response. Used only for debugging.
62std::string GetResponseHeadersAsString(const URLFetcher* url_fetcher) {
63  // net::HttpResponseHeaders::raw_headers(), as the name implies, stores
64  // all headers in their raw format, i.e each header is null-terminated.
65  // So logging raw_headers() only shows the first header, which is probably
66  // the status line.  GetNormalizedHeaders, on the other hand, will show all
67  // the headers, one per line, which is probably what we want.
68  std::string headers;
69  // Check that response code indicates response headers are valid (i.e. not
70  // malformed) before we retrieve the headers.
71  if (url_fetcher->GetResponseCode() == URLFetcher::RESPONSE_CODE_INVALID) {
72    headers.assign("Response headers are malformed!!");
73  } else {
74    url_fetcher->GetResponseHeaders()->GetNormalizedHeaders(&headers);
75  }
76  return headers;
77}
78
79bool IsSuccessfulResponseCode(int response_code) {
80  return 200 <= response_code && response_code <= 299;
81}
82
83}  // namespace
84
85namespace google_apis {
86
87scoped_ptr<base::Value> ParseJson(const std::string& json) {
88  int error_code = -1;
89  std::string error_message;
90  scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
91      json, base::JSON_PARSE_RFC, &error_code, &error_message));
92
93  if (!value.get()) {
94    std::string trimmed_json;
95    if (json.size() < 80) {
96      trimmed_json  = json;
97    } else {
98      // Take the first 50 and the last 10 bytes.
99      trimmed_json = base::StringPrintf(
100          "%s [%s bytes] %s",
101          json.substr(0, 50).c_str(),
102          base::Uint64ToString(json.size() - 60).c_str(),
103          json.substr(json.size() - 10).c_str());
104    }
105    LOG(WARNING) << "Error while parsing entry response: " << error_message
106                 << ", code: " << error_code << ", json:\n" << trimmed_json;
107  }
108  return value.Pass();
109}
110
111//=========================== ResponseWriter ==================================
112ResponseWriter::ResponseWriter(base::SequencedTaskRunner* file_task_runner,
113                               const base::FilePath& file_path,
114                               const GetContentCallback& get_content_callback)
115    : get_content_callback_(get_content_callback),
116      weak_ptr_factory_(this) {
117  if (!file_path.empty()) {
118    file_writer_.reset(
119        new net::URLFetcherFileWriter(file_task_runner, file_path));
120  }
121}
122
123ResponseWriter::~ResponseWriter() {
124}
125
126void ResponseWriter::DisownFile() {
127  DCHECK(file_writer_);
128  file_writer_->DisownFile();
129}
130
131int ResponseWriter::Initialize(const net::CompletionCallback& callback) {
132  if (file_writer_)
133    return file_writer_->Initialize(callback);
134
135  data_.clear();
136  return net::OK;
137}
138
139int ResponseWriter::Write(net::IOBuffer* buffer,
140                          int num_bytes,
141                          const net::CompletionCallback& callback) {
142  if (!get_content_callback_.is_null()) {
143    get_content_callback_.Run(
144        HTTP_SUCCESS,
145        make_scoped_ptr(new std::string(buffer->data(), num_bytes)));
146  }
147
148  if (file_writer_) {
149    const int result = file_writer_->Write(
150        buffer, num_bytes,
151        base::Bind(&ResponseWriter::DidWrite,
152                   weak_ptr_factory_.GetWeakPtr(),
153                   make_scoped_refptr(buffer), callback));
154    if (result != net::ERR_IO_PENDING)
155      DidWrite(buffer, net::CompletionCallback(), result);
156    return result;
157  }
158
159  data_.append(buffer->data(), num_bytes);
160  return num_bytes;
161}
162
163int ResponseWriter::Finish(const net::CompletionCallback& callback) {
164  if (file_writer_)
165    return file_writer_->Finish(callback);
166
167  return net::OK;
168}
169
170void ResponseWriter::DidWrite(scoped_refptr<net::IOBuffer> buffer,
171                              const net::CompletionCallback& callback,
172                              int result) {
173  if (result > 0) {
174    // Even if file_writer_ is used, append the data to |data_|, so that it can
175    // be used to get error information in case of server side errors.
176    // The size limit is to avoid consuming too much redundant memory.
177    const size_t kMaxStringSize = 1024*1024;
178    if (data_.size() < kMaxStringSize) {
179      data_.append(buffer->data(), std::min(static_cast<size_t>(result),
180                                            kMaxStringSize - data_.size()));
181    }
182  }
183
184  if (!callback.is_null())
185    callback.Run(result);
186}
187
188//============================ UrlFetchRequestBase ===========================
189
190UrlFetchRequestBase::UrlFetchRequestBase(RequestSender* sender)
191    : re_authenticate_count_(0),
192      response_writer_(NULL),
193      sender_(sender),
194      error_code_(GDATA_OTHER_ERROR),
195      weak_ptr_factory_(this) {
196}
197
198UrlFetchRequestBase::~UrlFetchRequestBase() {}
199
200void UrlFetchRequestBase::Start(const std::string& access_token,
201                                const std::string& custom_user_agent,
202                                const ReAuthenticateCallback& callback) {
203  DCHECK(CalledOnValidThread());
204  DCHECK(!access_token.empty());
205  DCHECK(!callback.is_null());
206  DCHECK(re_authenticate_callback_.is_null());
207
208  re_authenticate_callback_ = callback;
209
210  GURL url = GetURL();
211  if (url.is_empty()) {
212    // Error is found on generating the url. Send the error message to the
213    // callback, and then return immediately without trying to connect
214    // to the server.
215    RunCallbackOnPrematureFailure(GDATA_OTHER_ERROR);
216    return;
217  }
218  DVLOG(1) << "URL: " << url.spec();
219
220  URLFetcher::RequestType request_type = GetRequestType();
221  url_fetcher_.reset(URLFetcher::Create(url, request_type, this));
222  url_fetcher_->SetRequestContext(sender_->url_request_context_getter());
223  // Always set flags to neither send nor save cookies.
224  url_fetcher_->SetLoadFlags(
225      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES |
226      net::LOAD_DISABLE_CACHE);
227
228  base::FilePath output_file_path;
229  GetContentCallback get_content_callback;
230  GetOutputFilePath(&output_file_path, &get_content_callback);
231  if (!get_content_callback.is_null())
232    get_content_callback = CreateRelayCallback(get_content_callback);
233  response_writer_ = new ResponseWriter(blocking_task_runner(),
234                                        output_file_path,
235                                        get_content_callback);
236  url_fetcher_->SaveResponseWithWriter(
237      scoped_ptr<net::URLFetcherResponseWriter>(response_writer_));
238
239  // Add request headers.
240  // Note that SetExtraRequestHeaders clears the current headers and sets it
241  // to the passed-in headers, so calling it for each header will result in
242  // only the last header being set in request headers.
243  if (!custom_user_agent.empty())
244    url_fetcher_->AddExtraRequestHeader("User-Agent: " + custom_user_agent);
245  url_fetcher_->AddExtraRequestHeader(kGDataVersionHeader);
246  url_fetcher_->AddExtraRequestHeader(
247      base::StringPrintf(kAuthorizationHeaderFormat, access_token.data()));
248  std::vector<std::string> headers = GetExtraRequestHeaders();
249  for (size_t i = 0; i < headers.size(); ++i) {
250    url_fetcher_->AddExtraRequestHeader(headers[i]);
251    DVLOG(1) << "Extra header: " << headers[i];
252  }
253
254  // Set upload data if available.
255  std::string upload_content_type;
256  std::string upload_content;
257  if (GetContentData(&upload_content_type, &upload_content)) {
258    url_fetcher_->SetUploadData(upload_content_type, upload_content);
259  } else {
260    base::FilePath local_file_path;
261    int64 range_offset = 0;
262    int64 range_length = 0;
263    if (GetContentFile(&local_file_path, &range_offset, &range_length,
264                       &upload_content_type)) {
265      url_fetcher_->SetUploadFilePath(
266          upload_content_type,
267          local_file_path,
268          range_offset,
269          range_length,
270          blocking_task_runner());
271    } else {
272      // Even if there is no content data, UrlFetcher requires to set empty
273      // upload data string for POST, PUT and PATCH methods, explicitly.
274      // It is because that most requests of those methods have non-empty
275      // body, and UrlFetcher checks whether it is actually not forgotten.
276      if (request_type == URLFetcher::POST ||
277          request_type == URLFetcher::PUT ||
278          request_type == URLFetcher::PATCH) {
279        // Set empty upload content-type and upload content, so that
280        // the request will have no "Content-type: " header and no content.
281        url_fetcher_->SetUploadData(std::string(), std::string());
282      }
283    }
284  }
285
286  url_fetcher_->Start();
287}
288
289URLFetcher::RequestType UrlFetchRequestBase::GetRequestType() const {
290  return URLFetcher::GET;
291}
292
293std::vector<std::string> UrlFetchRequestBase::GetExtraRequestHeaders() const {
294  return std::vector<std::string>();
295}
296
297bool UrlFetchRequestBase::GetContentData(std::string* upload_content_type,
298                                         std::string* upload_content) {
299  return false;
300}
301
302bool UrlFetchRequestBase::GetContentFile(base::FilePath* local_file_path,
303                                         int64* range_offset,
304                                         int64* range_length,
305                                         std::string* upload_content_type) {
306  return false;
307}
308
309void UrlFetchRequestBase::GetOutputFilePath(
310    base::FilePath* local_file_path,
311    GetContentCallback* get_content_callback) {
312}
313
314void UrlFetchRequestBase::Cancel() {
315  response_writer_ = NULL;
316  url_fetcher_.reset(NULL);
317  RunCallbackOnPrematureFailure(GDATA_CANCELLED);
318  sender_->RequestFinished(this);
319}
320
321GDataErrorCode UrlFetchRequestBase::GetErrorCode() {
322  return error_code_;
323}
324
325bool UrlFetchRequestBase::CalledOnValidThread() {
326  return thread_checker_.CalledOnValidThread();
327}
328
329base::SequencedTaskRunner* UrlFetchRequestBase::blocking_task_runner() const {
330  return sender_->blocking_task_runner();
331}
332
333void UrlFetchRequestBase::OnProcessURLFetchResultsComplete() {
334  sender_->RequestFinished(this);
335}
336
337void UrlFetchRequestBase::OnURLFetchComplete(const URLFetcher* source) {
338  DVLOG(1) << "Response headers:\n" << GetResponseHeadersAsString(source);
339
340  // Determine error code.
341  error_code_ = static_cast<GDataErrorCode>(source->GetResponseCode());
342  if (!source->GetStatus().is_success()) {
343    switch (source->GetStatus().error()) {
344      case net::ERR_NETWORK_CHANGED:
345        error_code_ = GDATA_NO_CONNECTION;
346        break;
347      default:
348        error_code_ = GDATA_OTHER_ERROR;
349    }
350  }
351
352  // The server may return detailed error status in JSON.
353  // See https://developers.google.com/drive/handle-errors
354  if (!IsSuccessfulResponseCode(error_code_)) {
355    DVLOG(1) << response_writer_->data();
356
357    const char kErrorKey[] = "error";
358    const char kErrorErrorsKey[] = "errors";
359    const char kErrorReasonKey[] = "reason";
360    const char kErrorMessageKey[] = "message";
361    const char kErrorReasonRateLimitExceeded[] = "rateLimitExceeded";
362    const char kErrorReasonUserRateLimitExceeded[] = "userRateLimitExceeded";
363    const char kErrorReasonQuotaExceeded[] = "quotaExceeded";
364
365    scoped_ptr<base::Value> value(ParseJson(response_writer_->data()));
366    base::DictionaryValue* dictionary = NULL;
367    base::DictionaryValue* error = NULL;
368    if (value &&
369        value->GetAsDictionary(&dictionary) &&
370        dictionary->GetDictionaryWithoutPathExpansion(kErrorKey, &error)) {
371      // Get error message.
372      std::string message;
373      error->GetStringWithoutPathExpansion(kErrorMessageKey, &message);
374      DLOG(ERROR) << "code: " << error_code_ << ", message: " << message;
375
376      // Override the error code based on the reason of the first error.
377      base::ListValue* errors = NULL;
378      base::DictionaryValue* first_error = NULL;
379      if (error->GetListWithoutPathExpansion(kErrorErrorsKey, &errors) &&
380          errors->GetDictionary(0, &first_error)) {
381        std::string reason;
382        first_error->GetStringWithoutPathExpansion(kErrorReasonKey, &reason);
383        if (reason == kErrorReasonRateLimitExceeded ||
384            reason == kErrorReasonUserRateLimitExceeded)
385          error_code_ = HTTP_SERVICE_UNAVAILABLE;
386        if (reason == kErrorReasonQuotaExceeded)
387          error_code_ = GDATA_NO_SPACE;
388      }
389    }
390  }
391
392  // Handle authentication failure.
393  if (error_code_ == HTTP_UNAUTHORIZED) {
394    if (++re_authenticate_count_ <= kMaxReAuthenticateAttemptsPerRequest) {
395      // Reset re_authenticate_callback_ so Start() can be called again.
396      ReAuthenticateCallback callback = re_authenticate_callback_;
397      re_authenticate_callback_.Reset();
398      callback.Run(this);
399      return;
400    }
401
402    OnAuthFailed(error_code_);
403    return;
404  }
405
406  // Overridden by each specialization
407  ProcessURLFetchResults(source);
408}
409
410void UrlFetchRequestBase::OnAuthFailed(GDataErrorCode code) {
411  RunCallbackOnPrematureFailure(code);
412  sender_->RequestFinished(this);
413}
414
415base::WeakPtr<AuthenticatedRequestInterface>
416UrlFetchRequestBase::GetWeakPtr() {
417  return weak_ptr_factory_.GetWeakPtr();
418}
419
420//============================ EntryActionRequest ============================
421
422EntryActionRequest::EntryActionRequest(RequestSender* sender,
423                                       const EntryActionCallback& callback)
424    : UrlFetchRequestBase(sender),
425      callback_(callback) {
426  DCHECK(!callback_.is_null());
427}
428
429EntryActionRequest::~EntryActionRequest() {}
430
431void EntryActionRequest::ProcessURLFetchResults(const URLFetcher* source) {
432  callback_.Run(GetErrorCode());
433  OnProcessURLFetchResultsComplete();
434}
435
436void EntryActionRequest::RunCallbackOnPrematureFailure(GDataErrorCode code) {
437  callback_.Run(code);
438}
439
440//========================= InitiateUploadRequestBase ========================
441
442InitiateUploadRequestBase::InitiateUploadRequestBase(
443    RequestSender* sender,
444    const InitiateUploadCallback& callback,
445    const std::string& content_type,
446    int64 content_length)
447    : UrlFetchRequestBase(sender),
448      callback_(callback),
449      content_type_(content_type),
450      content_length_(content_length) {
451  DCHECK(!callback_.is_null());
452  DCHECK(!content_type_.empty());
453  DCHECK_GE(content_length_, 0);
454}
455
456InitiateUploadRequestBase::~InitiateUploadRequestBase() {}
457
458void InitiateUploadRequestBase::ProcessURLFetchResults(
459    const URLFetcher* source) {
460  GDataErrorCode code = GetErrorCode();
461
462  std::string upload_location;
463  if (code == HTTP_SUCCESS) {
464    // Retrieve value of the first "Location" header.
465    source->GetResponseHeaders()->EnumerateHeader(NULL,
466                                                  kUploadResponseLocation,
467                                                  &upload_location);
468  }
469
470  callback_.Run(code, GURL(upload_location));
471  OnProcessURLFetchResultsComplete();
472}
473
474void InitiateUploadRequestBase::RunCallbackOnPrematureFailure(
475    GDataErrorCode code) {
476  callback_.Run(code, GURL());
477}
478
479std::vector<std::string>
480InitiateUploadRequestBase::GetExtraRequestHeaders() const {
481  std::vector<std::string> headers;
482  headers.push_back(kUploadContentType + content_type_);
483  headers.push_back(
484      kUploadContentLength + base::Int64ToString(content_length_));
485  return headers;
486}
487
488//============================ UploadRangeResponse =============================
489
490UploadRangeResponse::UploadRangeResponse()
491    : code(HTTP_SUCCESS),
492      start_position_received(0),
493      end_position_received(0) {
494}
495
496UploadRangeResponse::UploadRangeResponse(GDataErrorCode code,
497                                         int64 start_position_received,
498                                         int64 end_position_received)
499    : code(code),
500      start_position_received(start_position_received),
501      end_position_received(end_position_received) {
502}
503
504UploadRangeResponse::~UploadRangeResponse() {
505}
506
507//========================== UploadRangeRequestBase ==========================
508
509UploadRangeRequestBase::UploadRangeRequestBase(RequestSender* sender,
510                                               const GURL& upload_url)
511    : UrlFetchRequestBase(sender),
512      upload_url_(upload_url),
513      weak_ptr_factory_(this) {
514}
515
516UploadRangeRequestBase::~UploadRangeRequestBase() {}
517
518GURL UploadRangeRequestBase::GetURL() const {
519  // This is very tricky to get json from this request. To do that, &alt=json
520  // has to be appended not here but in InitiateUploadRequestBase::GetURL().
521  return upload_url_;
522}
523
524URLFetcher::RequestType UploadRangeRequestBase::GetRequestType() const {
525  return URLFetcher::PUT;
526}
527
528void UploadRangeRequestBase::ProcessURLFetchResults(
529    const URLFetcher* source) {
530  GDataErrorCode code = GetErrorCode();
531  net::HttpResponseHeaders* hdrs = source->GetResponseHeaders();
532
533  if (code == HTTP_RESUME_INCOMPLETE) {
534    // Retrieve value of the first "Range" header.
535    // The Range header is appeared only if there is at least one received
536    // byte. So, initialize the positions by 0 so that the [0,0) will be
537    // returned via the |callback_| for empty data case.
538    int64 start_position_received = 0;
539    int64 end_position_received = 0;
540    std::string range_received;
541    hdrs->EnumerateHeader(NULL, kUploadResponseRange, &range_received);
542    if (!range_received.empty()) {  // Parse the range header.
543      std::vector<net::HttpByteRange> ranges;
544      if (net::HttpUtil::ParseRangeHeader(range_received, &ranges) &&
545          !ranges.empty() ) {
546        // We only care about the first start-end pair in the range.
547        //
548        // Range header represents the range inclusively, while we are treating
549        // ranges exclusively (i.e., end_position_received should be one passed
550        // the last valid index). So "+ 1" is added.
551        start_position_received = ranges[0].first_byte_position();
552        end_position_received = ranges[0].last_byte_position() + 1;
553      }
554    }
555    // The Range header has the received data range, so the start position
556    // should be always 0.
557    DCHECK_EQ(start_position_received, 0);
558
559    OnRangeRequestComplete(UploadRangeResponse(code,
560                                               start_position_received,
561                                               end_position_received),
562                           scoped_ptr<base::Value>());
563
564    OnProcessURLFetchResultsComplete();
565  } else if (code == HTTP_CREATED || code == HTTP_SUCCESS) {
566    // The upload is successfully done. Parse the response which should be
567    // the entry's metadata.
568    ParseJsonOnBlockingPool(blocking_task_runner(),
569                            response_writer()->data(),
570                            base::Bind(&UploadRangeRequestBase::OnDataParsed,
571                                       weak_ptr_factory_.GetWeakPtr(),
572                                       code));
573  } else {
574    // Failed to upload. Run callbacks to notify the error.
575    OnRangeRequestComplete(
576        UploadRangeResponse(code, -1, -1), scoped_ptr<base::Value>());
577    OnProcessURLFetchResultsComplete();
578  }
579}
580
581void UploadRangeRequestBase::OnDataParsed(GDataErrorCode code,
582                                          scoped_ptr<base::Value> value) {
583  DCHECK(CalledOnValidThread());
584  DCHECK(code == HTTP_CREATED || code == HTTP_SUCCESS);
585
586  OnRangeRequestComplete(UploadRangeResponse(code, -1, -1), value.Pass());
587  OnProcessURLFetchResultsComplete();
588}
589
590void UploadRangeRequestBase::RunCallbackOnPrematureFailure(
591    GDataErrorCode code) {
592  OnRangeRequestComplete(
593      UploadRangeResponse(code, 0, 0), scoped_ptr<base::Value>());
594}
595
596//========================== ResumeUploadRequestBase =========================
597
598ResumeUploadRequestBase::ResumeUploadRequestBase(
599    RequestSender* sender,
600    const GURL& upload_location,
601    int64 start_position,
602    int64 end_position,
603    int64 content_length,
604    const std::string& content_type,
605    const base::FilePath& local_file_path)
606    : UploadRangeRequestBase(sender, upload_location),
607      start_position_(start_position),
608      end_position_(end_position),
609      content_length_(content_length),
610      content_type_(content_type),
611      local_file_path_(local_file_path) {
612  DCHECK_LE(start_position_, end_position_);
613}
614
615ResumeUploadRequestBase::~ResumeUploadRequestBase() {}
616
617std::vector<std::string>
618ResumeUploadRequestBase::GetExtraRequestHeaders() const {
619  if (content_length_ == 0) {
620    // For uploading an empty document, just PUT an empty content.
621    DCHECK_EQ(start_position_, 0);
622    DCHECK_EQ(end_position_, 0);
623    return std::vector<std::string>();
624  }
625
626  // The header looks like
627  // Content-Range: bytes <start_position>-<end_position>/<content_length>
628  // for example:
629  // Content-Range: bytes 7864320-8388607/13851821
630  // The header takes inclusive range, so we adjust by "end_position - 1".
631  DCHECK_GE(start_position_, 0);
632  DCHECK_GT(end_position_, 0);
633  DCHECK_GE(content_length_, 0);
634
635  std::vector<std::string> headers;
636  headers.push_back(
637      std::string(kUploadContentRange) +
638      base::Int64ToString(start_position_) + "-" +
639      base::Int64ToString(end_position_ - 1) + "/" +
640      base::Int64ToString(content_length_));
641  return headers;
642}
643
644bool ResumeUploadRequestBase::GetContentFile(
645    base::FilePath* local_file_path,
646    int64* range_offset,
647    int64* range_length,
648    std::string* upload_content_type) {
649  if (start_position_ == end_position_) {
650    // No content data.
651    return false;
652  }
653
654  *local_file_path = local_file_path_;
655  *range_offset = start_position_;
656  *range_length = end_position_ - start_position_;
657  *upload_content_type = content_type_;
658  return true;
659}
660
661//======================== GetUploadStatusRequestBase ========================
662
663GetUploadStatusRequestBase::GetUploadStatusRequestBase(RequestSender* sender,
664                                                       const GURL& upload_url,
665                                                       int64 content_length)
666    : UploadRangeRequestBase(sender, upload_url),
667      content_length_(content_length) {}
668
669GetUploadStatusRequestBase::~GetUploadStatusRequestBase() {}
670
671std::vector<std::string>
672GetUploadStatusRequestBase::GetExtraRequestHeaders() const {
673  // The header looks like
674  // Content-Range: bytes */<content_length>
675  // for example:
676  // Content-Range: bytes */13851821
677  DCHECK_GE(content_length_, 0);
678
679  std::vector<std::string> headers;
680  headers.push_back(
681      std::string(kUploadContentRange) + "*/" +
682      base::Int64ToString(content_length_));
683  return headers;
684}
685
686//============================ DownloadFileRequestBase =========================
687
688DownloadFileRequestBase::DownloadFileRequestBase(
689    RequestSender* sender,
690    const DownloadActionCallback& download_action_callback,
691    const GetContentCallback& get_content_callback,
692    const ProgressCallback& progress_callback,
693    const GURL& download_url,
694    const base::FilePath& output_file_path)
695    : UrlFetchRequestBase(sender),
696      download_action_callback_(download_action_callback),
697      get_content_callback_(get_content_callback),
698      progress_callback_(progress_callback),
699      download_url_(download_url),
700      output_file_path_(output_file_path) {
701  DCHECK(!download_action_callback_.is_null());
702  DCHECK(!output_file_path_.empty());
703  // get_content_callback may be null.
704}
705
706DownloadFileRequestBase::~DownloadFileRequestBase() {}
707
708// Overridden from UrlFetchRequestBase.
709GURL DownloadFileRequestBase::GetURL() const {
710  return download_url_;
711}
712
713void DownloadFileRequestBase::GetOutputFilePath(
714    base::FilePath* local_file_path,
715    GetContentCallback* get_content_callback) {
716  // Configure so that the downloaded content is saved to |output_file_path_|.
717  *local_file_path = output_file_path_;
718  *get_content_callback = get_content_callback_;
719}
720
721void DownloadFileRequestBase::OnURLFetchDownloadProgress(
722    const URLFetcher* source,
723    int64 current,
724    int64 total) {
725  if (!progress_callback_.is_null())
726    progress_callback_.Run(current, total);
727}
728
729void DownloadFileRequestBase::ProcessURLFetchResults(const URLFetcher* source) {
730  GDataErrorCode code = GetErrorCode();
731
732  // Take over the ownership of the the downloaded temp file.
733  base::FilePath temp_file;
734  if (code == HTTP_SUCCESS) {
735    response_writer()->DisownFile();
736    temp_file = output_file_path_;
737  }
738
739  download_action_callback_.Run(code, temp_file);
740  OnProcessURLFetchResultsComplete();
741}
742
743void DownloadFileRequestBase::RunCallbackOnPrematureFailure(
744    GDataErrorCode code) {
745  download_action_callback_.Run(code, base::FilePath());
746}
747
748}  // namespace google_apis
749