| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/data_plan_provider.h" |
| |
| #include <vector> |
| |
| #include <base/json/json_reader.h> // NOLINT |
| #include <base/memory/scoped_ptr.h> // NOLINT |
| #include <base/values.h> // NOLINT |
| #include <glog/logging.h> // NOLINT |
| |
| #include "src/libcurl_http_fetcher.h" |
| |
| using std::vector; |
| |
| namespace cashew { |
| |
| DataPlanProvider::DataPlanProvider(const std::string& carrier) |
| : carrier_(carrier), delegate_(NULL), |
| fetcher_(CHECK_NOTNULL(new(std::nothrow) LibcurlHttpFetcher())), |
| request_in_progress_(false) { |
| fetcher_->set_delegate(this); |
| } |
| |
| DataPlanProvider::~DataPlanProvider() { |
| CancelPendingRequests(); |
| } |
| |
| const std::string& DataPlanProvider::GetCarrier() const { |
| return carrier_; |
| } |
| |
| const std::string& DataPlanProvider::GetUsageUrl() const { |
| return usage_url_; |
| } |
| |
| void DataPlanProvider::SetUsageUrl(const std::string& usage_url) { |
| LOG(INFO) << carrier_ << ": SetUsageUrl: url = " << usage_url; |
| if (usage_url_ != usage_url) { |
| usage_url_ = usage_url; |
| CancelPendingRequests(); |
| } |
| } |
| |
| void DataPlanProvider::SetHttpProxy(const std::string& http_proxy) { |
| LOG(INFO) << carrier_ << ": SetHttpProxy: http_proxy = " << http_proxy; |
| if (http_proxy_ != http_proxy) { |
| http_proxy_ = http_proxy; |
| CancelPendingRequests(); |
| } |
| } |
| |
| void DataPlanProvider::SetDelegate(DataPlanProviderDelegate *delegate) { |
| LOG(INFO) << carrier_ << ": SetDelegate"; |
| delegate_ = delegate; |
| if (delegate_ == NULL) { |
| CancelPendingRequests(); |
| } |
| } |
| |
| void DataPlanProvider::SetNameServers(const vector<std::string>& nameservers) { |
| fetcher_->set_nameservers(nameservers); |
| } |
| |
| bool DataPlanProvider::RequestUsageUpdate() { |
| if (usage_url_.empty()) { |
| LOG(WARNING) << carrier_ << ": RequestUsageUpdate: no usage url"; |
| return false; |
| } |
| if (delegate_ == NULL) { |
| LOG(WARNING) << carrier_ << ": RequestUsageUpdate: no delegate"; |
| return false; |
| } |
| if (request_in_progress_) { |
| LOG(INFO) << carrier_ |
| << ": RequestUsageUpdate: request already in progress"; |
| // we'll satisfy this request with the result from the in-flight one |
| return true; |
| } |
| DCHECK(response_buffer_.empty()); |
| LOG(INFO) << carrier_ << ": RequestUsageUpdate: initiating request"; |
| request_in_progress_ = true; |
| fetcher_->BeginTransfer(usage_url_, http_proxy_); |
| return true; |
| } |
| |
| // HttpFetcherDelegate methods |
| |
| void DataPlanProvider::ReceivedBytes(HttpFetcher *fetcher, const char *bytes, |
| int length) { |
| DCHECK(fetcher != NULL); |
| DCHECK(bytes != NULL); |
| DCHECK_GE(length, 0); |
| if (fetcher != fetcher_) { |
| LOG(WARNING) << carrier_ << ": ReceivedBytes: wrong fetcher"; |
| return; |
| } |
| DCHECK(request_in_progress_); |
| LOG(INFO) << carrier_ << ": ReceivedBytes: length = " << length; |
| response_buffer_.reserve(response_buffer_.size() + length); |
| response_buffer_.insert(response_buffer_.end(), bytes, bytes + length); |
| } |
| |
| void DataPlanProvider::TransferComplete(HttpFetcher *fetcher, |
| bool successful) { |
| DCHECK(fetcher != NULL); |
| if (fetcher != fetcher_) { |
| LOG(WARNING) << carrier_ << ": TransferComplete: wrong fetcher"; |
| return; |
| } |
| DCHECK(request_in_progress_); |
| DCHECK(delegate_ != NULL); |
| LOG(INFO) << carrier_ << ": TransferComplete: result = " << successful; |
| if (!successful) { |
| LOG(WARNING) << carrier_ << ": TransferComplete: failed: response code = " |
| << fetcher_->http_response_code(); |
| response_buffer_.clear(); |
| request_in_progress_ = false; |
| delegate_->OnRequestComplete(this, false, NULL); |
| return; |
| } |
| LOG(INFO) << carrier_ << ": TransferComplete: success: response code = " |
| << fetcher_->http_response_code(); |
| |
| // carrier usage API has (we hope) given us a JSON-encoded result |
| // log it and then parse it, converting it into a tree of Value nodes |
| const std::string json_encoded_usage_update(response_buffer_.begin(), |
| response_buffer_.end()); |
| response_buffer_.clear(); |
| request_in_progress_ = false; |
| |
| LOG(INFO) << carrier_ << ": TransferComplete: response:\n" |
| << json_encoded_usage_update; |
| |
| int error_code; |
| std::string error_msg; |
| scoped_ptr<Value> parsed_usage_update( |
| base::JSONReader::ReadAndReturnError(json_encoded_usage_update, true, |
| &error_code, &error_msg)); |
| if (parsed_usage_update == NULL) { |
| LOG(WARNING) << carrier_ << ": TransferComplete: could not parse json: " |
| << "error code " << error_code << ": " << error_msg; |
| delegate_->OnRequestComplete(this, false, NULL); |
| return; |
| } |
| LOG(INFO) << carrier_ << ": TransferComplete: successfully parsed json"; |
| delegate_->OnRequestComplete(this, true, parsed_usage_update.get()); |
| } |
| |
| void DataPlanProvider::CancelPendingRequests() { |
| if (request_in_progress_) { |
| fetcher_->TerminateTransfer(); |
| response_buffer_.clear(); |
| request_in_progress_ = false; |
| } |
| } |
| |
| // private methods |
| |
| } // namespace cashew |