Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / local_discovery / privet_url_fetcher.cc
1 // Copyright 2013 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 "chrome/browser/local_discovery/privet_url_fetcher.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/json/json_reader.h"
11 #include "base/memory/singleton.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/rand_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/local_discovery/privet_constants.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "net/http/http_status_code.h"
19 #include "net/url_request/url_request_status.h"
20
21 namespace local_discovery {
22
23 namespace {
24
25 typedef std::map<std::string, std::string> TokenMap;
26
27 struct TokenMapHolder {
28  public:
29   static TokenMapHolder* GetInstance() {
30     return Singleton<TokenMapHolder>::get();
31   }
32
33   TokenMap map;
34 };
35
36 const char kXPrivetTokenHeaderPrefix[] = "X-Privet-Token: ";
37 const char kXPrivetAuthTokenHeaderPrefix[] = "X-Privet-Auth: ";
38 const char kRangeHeaderFormat[] = "Range: bytes=%d-%d";
39 const char kXPrivetEmptyToken[] = "\"\"";
40 const char kPrivetAuthTokenUnknown[] = "Unknown";
41 const int kPrivetMaxRetries = 20;
42 const int kPrivetTimeoutOnError = 5;
43 const int kHTTPErrorCodeInvalidXPrivetToken = 418;
44
45 std::string MakeRangeHeader(int start, int end) {
46   DCHECK_GE(start, 0);
47   DCHECK_GT(end, 0);
48   DCHECK_GT(end, start);
49   return base::StringPrintf(kRangeHeaderFormat, start, end);
50 }
51
52 }  // namespace
53
54 void PrivetURLFetcher::Delegate::OnNeedPrivetToken(
55     PrivetURLFetcher* fetcher,
56     const TokenCallback& callback) {
57   OnError(fetcher, TOKEN_ERROR);
58 }
59
60 std::string PrivetURLFetcher::Delegate::GetAuthToken() {
61   return kPrivetAuthTokenUnknown;
62 }
63
64 bool PrivetURLFetcher::Delegate::OnRawData(PrivetURLFetcher* fetcher,
65                                            bool response_is_file,
66                                            const std::string& data_string,
67                                            const base::FilePath& data_file) {
68   return false;
69 }
70
71 PrivetURLFetcher::PrivetURLFetcher(
72     const GURL& url,
73     net::URLFetcher::RequestType request_type,
74     net::URLRequestContextGetter* request_context,
75     PrivetURLFetcher::Delegate* delegate)
76     : url_(url),
77       request_type_(request_type),
78       request_context_(request_context),
79       delegate_(delegate),
80       do_not_retry_on_transient_error_(false),
81       send_empty_privet_token_(false),
82       has_byte_range_(false),
83       make_response_file_(false),
84       v3_mode_(false),
85       byte_range_start_(0),
86       byte_range_end_(0),
87       tries_(0),
88       weak_factory_(this) {
89 }
90
91 PrivetURLFetcher::~PrivetURLFetcher() {
92 }
93
94 // static
95 void PrivetURLFetcher::SetTokenForHost(const std::string& host,
96                                        const std::string& token) {
97   TokenMapHolder::GetInstance()->map[host] = token;
98 }
99
100 // static
101 void PrivetURLFetcher::ResetTokenMapForTests() {
102   TokenMapHolder::GetInstance()->map.clear();
103 }
104
105 void PrivetURLFetcher::DoNotRetryOnTransientError() {
106   DCHECK(tries_ == 0);
107   do_not_retry_on_transient_error_ = true;
108 }
109
110 void PrivetURLFetcher::SendEmptyPrivetToken() {
111   DCHECK(tries_ == 0);
112   send_empty_privet_token_ = true;
113 }
114
115 std::string PrivetURLFetcher::GetPrivetAccessToken() {
116   if (send_empty_privet_token_) {
117     return std::string();
118   }
119
120   TokenMapHolder* token_map_holder = TokenMapHolder::GetInstance();
121   TokenMap::iterator found = token_map_holder->map.find(GetHostString());
122   return found != token_map_holder->map.end() ? found->second : std::string();
123 }
124
125 std::string PrivetURLFetcher::GetHostString() {
126   return url_.GetOrigin().spec();
127 }
128
129 void PrivetURLFetcher::SaveResponseToFile() {
130   DCHECK(tries_ == 0);
131   make_response_file_ = true;
132 }
133
134 void PrivetURLFetcher::V3Mode() {
135   v3_mode_ = true;
136 }
137
138 void PrivetURLFetcher::SetByteRange(int start, int end) {
139   DCHECK(tries_ == 0);
140   byte_range_start_ = start;
141   byte_range_end_ = end;
142   has_byte_range_ = true;
143 }
144
145 void PrivetURLFetcher::Try() {
146   tries_++;
147   if (tries_ < kPrivetMaxRetries) {
148
149
150     url_fetcher_.reset(net::URLFetcher::Create(url_, request_type_, this));
151     url_fetcher_->SetRequestContext(request_context_.get());
152
153     if (v3_mode_) {
154       std::string auth_token = delegate_->GetAuthToken();
155
156       url_fetcher_->AddExtraRequestHeader(
157           std::string(kXPrivetAuthTokenHeaderPrefix) + auth_token);
158     } else {
159       std::string token = GetPrivetAccessToken();
160
161       if (token.empty())
162         token = kXPrivetEmptyToken;
163
164       url_fetcher_->AddExtraRequestHeader(
165           std::string(kXPrivetTokenHeaderPrefix) + token);
166     }
167
168     if (has_byte_range_) {
169       url_fetcher_->AddExtraRequestHeader(
170           MakeRangeHeader(byte_range_start_, byte_range_end_));
171     }
172
173     if (make_response_file_) {
174       url_fetcher_->SaveResponseToTemporaryFile(
175           content::BrowserThread::GetMessageLoopProxyForThread(
176               content::BrowserThread::FILE));
177     }
178
179     // URLFetcher requires us to set upload data for POST requests.
180     if (request_type_ == net::URLFetcher::POST) {
181       if (!upload_file_path_.empty()) {
182         url_fetcher_->SetUploadFilePath(
183             upload_content_type_,
184             upload_file_path_,
185             0 /*offset*/,
186             kuint64max /*length*/,
187             content::BrowserThread::GetMessageLoopProxyForThread(
188                 content::BrowserThread::FILE));
189       } else {
190         url_fetcher_->SetUploadData(upload_content_type_, upload_data_);
191       }
192     }
193
194     url_fetcher_->Start();
195   } else {
196     delegate_->OnError(this, RETRY_ERROR);
197   }
198 }
199
200 void PrivetURLFetcher::Start() {
201   DCHECK_EQ(tries_, 0);  // We haven't called |Start()| yet.
202
203   if (!send_empty_privet_token_ && !v3_mode_) {
204     std::string privet_access_token;
205     privet_access_token = GetPrivetAccessToken();
206     if (privet_access_token.empty()) {
207       RequestTokenRefresh();
208       return;
209     }
210   }
211
212   Try();
213 }
214
215 void PrivetURLFetcher::SetUploadData(const std::string& upload_content_type,
216                                      const std::string& upload_data) {
217   DCHECK(upload_file_path_.empty());
218   upload_content_type_ = upload_content_type;
219   upload_data_ = upload_data;
220 }
221
222 void PrivetURLFetcher::SetUploadFilePath(
223     const std::string& upload_content_type,
224     const base::FilePath& upload_file_path) {
225   DCHECK(upload_data_.empty());
226   upload_content_type_ = upload_content_type;
227   upload_file_path_ = upload_file_path;
228 }
229
230 void PrivetURLFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
231   if (source->GetResponseCode() == net::HTTP_SERVICE_UNAVAILABLE) {
232     ScheduleRetry(kPrivetTimeoutOnError);
233     return;
234   }
235
236   if (!OnURLFetchCompleteDoNotParseData(source)) {
237     // Byte ranges should only be used when we're not parsing the data
238     // as JSON.
239     DCHECK(!has_byte_range_);
240
241     // We should only be saving raw data to a file.
242     DCHECK(!make_response_file_);
243
244     OnURLFetchCompleteParseData(source);
245   }
246 }
247
248 // Note that this function returns "true" in error cases to indicate
249 // that it has fully handled the responses.
250 bool PrivetURLFetcher::OnURLFetchCompleteDoNotParseData(
251     const net::URLFetcher* source) {
252   if (source->GetResponseCode() == kHTTPErrorCodeInvalidXPrivetToken) {
253     RequestTokenRefresh();
254     return true;
255   }
256
257   if (source->GetResponseCode() != net::HTTP_OK &&
258       source->GetResponseCode() != net::HTTP_PARTIAL_CONTENT) {
259     delegate_->OnError(this, RESPONSE_CODE_ERROR);
260     return true;
261   }
262
263   if (make_response_file_) {
264     base::FilePath response_file_path;
265
266     if (!source->GetResponseAsFilePath(true, &response_file_path)) {
267       delegate_->OnError(this, URL_FETCH_ERROR);
268       return true;
269     }
270
271     return delegate_->OnRawData(this, true, std::string(), response_file_path);
272   } else {
273     std::string response_str;
274
275     if (!source->GetResponseAsString(&response_str)) {
276       delegate_->OnError(this, URL_FETCH_ERROR);
277       return true;
278     }
279
280     return delegate_->OnRawData(this, false, response_str, base::FilePath());
281   }
282 }
283
284 void PrivetURLFetcher::OnURLFetchCompleteParseData(
285     const net::URLFetcher* source) {
286   // Response contains error description.
287   bool is_error_response = false;
288   if (v3_mode_ && source->GetResponseCode() == net::HTTP_BAD_REQUEST) {
289     is_error_response = true;
290   } else if (source->GetResponseCode() != net::HTTP_OK) {
291     delegate_->OnError(this, RESPONSE_CODE_ERROR);
292     return;
293   }
294
295   std::string response_str;
296   if (!source->GetResponseAsString(&response_str)) {
297     delegate_->OnError(this, URL_FETCH_ERROR);
298     return;
299   }
300
301   base::JSONReader json_reader(base::JSON_ALLOW_TRAILING_COMMAS);
302   scoped_ptr<base::Value> value;
303
304   value.reset(json_reader.ReadToValue(response_str));
305
306   if (!value) {
307     delegate_->OnError(this, JSON_PARSE_ERROR);
308     return;
309   }
310
311   const base::DictionaryValue* dictionary_value = NULL;
312
313   if (!value->GetAsDictionary(&dictionary_value)) {
314     delegate_->OnError(this, JSON_PARSE_ERROR);
315     return;
316   }
317
318   std::string error;
319   if (!v3_mode_ && dictionary_value->GetString(kPrivetKeyError, &error)) {
320     if (error == kPrivetErrorInvalidXPrivetToken) {
321       RequestTokenRefresh();
322       return;
323     } else if (PrivetErrorTransient(error)) {
324       if (!do_not_retry_on_transient_error_) {
325         int timeout_seconds;
326         if (!dictionary_value->GetInteger(kPrivetKeyTimeout,
327                                           &timeout_seconds)) {
328           timeout_seconds = kPrivetDefaultTimeout;
329         }
330
331         ScheduleRetry(timeout_seconds);
332         return;
333       }
334     }
335     is_error_response = true;
336   }
337
338   delegate_->OnParsedJson(this, *dictionary_value, is_error_response);
339 }
340
341 void PrivetURLFetcher::ScheduleRetry(int timeout_seconds) {
342   double random_scaling_factor =
343       1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition;
344
345   int timeout_seconds_randomized =
346       static_cast<int>(timeout_seconds * random_scaling_factor);
347
348   timeout_seconds_randomized =
349       std::max(timeout_seconds_randomized, kPrivetMinimumTimeout);
350
351   base::MessageLoop::current()->PostDelayedTask(
352       FROM_HERE,
353       base::Bind(&PrivetURLFetcher::Try, weak_factory_.GetWeakPtr()),
354       base::TimeDelta::FromSeconds(timeout_seconds_randomized));
355 }
356
357 void PrivetURLFetcher::RequestTokenRefresh() {
358   delegate_->OnNeedPrivetToken(
359       this,
360       base::Bind(&PrivetURLFetcher::RefreshToken, weak_factory_.GetWeakPtr()));
361 }
362
363 void PrivetURLFetcher::RefreshToken(const std::string& token) {
364   if (token.empty()) {
365     delegate_->OnError(this, TOKEN_ERROR);
366   } else {
367     SetTokenForHost(GetHostString(), token);
368     Try();
369   }
370 }
371
372 bool PrivetURLFetcher::PrivetErrorTransient(const std::string& error) {
373   return (error == kPrivetErrorDeviceBusy) ||
374          (error == kPrivetV3ErrorDeviceBusy) ||
375          (error == kPrivetErrorPendingUserAction) ||
376          (error == kPrivetErrorPrinterBusy);
377 }
378
379 }  // namespace local_discovery