blob: d93c5c2b522edfad4e60969337356b87e9279acd [file] [log] [blame]
[email protected]677c90572008-12-10 09:03:151// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this
2// source code is governed by a BSD-style license that can be found in the
3// LICENSE file.
4
5#include "net/proxy/proxy_script_fetcher.h"
6
7#include "base/compiler_specific.h"
[email protected]13a279e2009-04-13 17:32:378#include "base/logging.h"
[email protected]677c90572008-12-10 09:03:159#include "base/message_loop.h"
[email protected]9dea9e1f2009-01-29 00:30:4710#include "base/ref_counted.h"
[email protected]677c90572008-12-10 09:03:1511#include "base/string_util.h"
[email protected]9dea9e1f2009-01-29 00:30:4712#include "net/base/io_buffer.h"
[email protected]677c90572008-12-10 09:03:1513#include "net/base/load_flags.h"
[email protected]597cf6e2009-05-29 09:43:2614#include "net/base/net_errors.h"
[email protected]e0ef2c22009-06-03 23:54:4415#include "net/http/http_response_headers.h"
[email protected]677c90572008-12-10 09:03:1516#include "net/url_request/url_request.h"
17
[email protected]f0a51fb52009-03-05 12:46:3818// TODO(eroman):
[email protected]677c90572008-12-10 09:03:1519// - Support auth-prompts.
20
21namespace net {
22
23namespace {
24
25// The maximum size (in bytes) allowed for a PAC script. Responses exceeding
26// this will fail with ERR_FILE_TOO_BIG.
27int max_response_bytes = 1048576; // 1 megabyte
28
29// The maximum duration (in milliseconds) allowed for fetching the PAC script.
30// Responses exceeding this will fail with ERR_TIMED_OUT.
31int max_duration_ms = 300000; // 5 minutes
32
[email protected]13a279e2009-04-13 17:32:3733// Returns true if |mime_type| is one of the known PAC mime type.
34bool IsPacMimeType(const std::string& mime_type) {
35 static const char * const kSupportedPacMimeTypes[] = {
36 "application/x-ns-proxy-autoconfig",
37 "application/x-javascript-config",
38 };
39 for (size_t i = 0; i < arraysize(kSupportedPacMimeTypes); ++i) {
40 if (LowerCaseEqualsASCII(mime_type, kSupportedPacMimeTypes[i]))
41 return true;
42 }
43 return false;
44}
45
[email protected]677c90572008-12-10 09:03:1546} // namespace
47
48class ProxyScriptFetcherImpl : public ProxyScriptFetcher,
49 public URLRequest::Delegate {
50 public:
51 // Creates a ProxyScriptFetcher that issues requests through
52 // |url_request_context|. |url_request_context| must remain valid for the
53 // lifetime of ProxyScriptFetcherImpl.
54 explicit ProxyScriptFetcherImpl(URLRequestContext* url_request_context);
55
56 virtual ~ProxyScriptFetcherImpl();
57
58 // ProxyScriptFetcher methods:
59
[email protected]620f5712009-08-04 22:43:1260 virtual int Fetch(const GURL& url, std::string* bytes,
61 CompletionCallback* callback);
[email protected]677c90572008-12-10 09:03:1562 virtual void Cancel();
63
64 // URLRequest::Delegate methods:
65
66 virtual void OnAuthRequired(URLRequest* request,
67 AuthChallengeInfo* auth_info);
68 virtual void OnSSLCertificateError(URLRequest* request, int cert_error,
69 X509Certificate* cert);
[email protected]677c90572008-12-10 09:03:1570 virtual void OnResponseStarted(URLRequest* request);
71 virtual void OnReadCompleted(URLRequest* request, int num_bytes);
72 virtual void OnResponseCompleted(URLRequest* request);
73
74 private:
75 // Read more bytes from the response.
76 void ReadBody(URLRequest* request);
77
78 // Called once the request has completed to notify the caller of
79 // |response_code_| and |response_bytes_|.
80 void FetchCompleted();
81
82 // Clear out the state for the current request.
83 void ResetCurRequestState();
84
85 // Callback for time-out task of request with id |id|.
86 void OnTimeout(int id);
87
88 // Factory for creating the time-out task. This takes care of revoking
89 // outstanding tasks when |this| is deleted.
90 ScopedRunnableMethodFactory<ProxyScriptFetcherImpl> task_factory_;
91
92 // The context used for making network requests.
93 URLRequestContext* url_request_context_;
94
95 // Buffer that URLRequest writes into.
96 enum { kBufSize = 4096 };
[email protected]9dea9e1f2009-01-29 00:30:4797 scoped_refptr<net::IOBuffer> buf_;
[email protected]677c90572008-12-10 09:03:1598
99 // The next ID to use for |cur_request_| (monotonically increasing).
100 int next_id_;
101
102 // The current (in progress) request, or NULL.
103 scoped_ptr<URLRequest> cur_request_;
104
105 // State for current request (only valid when |cur_request_| is not NULL):
106
107 // Unique ID for the current request.
108 int cur_request_id_;
109
110 // Callback to invoke on completion of the fetch.
111 CompletionCallback* callback_;
112
113 // Holds the error condition that was hit on the current request, or OK.
114 int result_code_;
115
116 // Holds the bytes read so far. Will not exceed |max_response_bytes|. This
117 // buffer is owned by the owner of |callback|.
118 std::string* result_bytes_;
119};
120
121ProxyScriptFetcherImpl::ProxyScriptFetcherImpl(
122 URLRequestContext* url_request_context)
123 : ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
124 url_request_context_(url_request_context),
[email protected]9dea9e1f2009-01-29 00:30:47125 buf_(new net::IOBuffer(kBufSize)),
[email protected]677c90572008-12-10 09:03:15126 next_id_(0),
127 cur_request_(NULL),
128 cur_request_id_(0),
129 callback_(NULL),
130 result_code_(OK),
131 result_bytes_(NULL) {
132 DCHECK(url_request_context);
133}
134
135ProxyScriptFetcherImpl::~ProxyScriptFetcherImpl() {
136 // The URLRequest's destructor will cancel the outstanding request, and
137 // ensure that the delegate (this) is not called again.
138}
139
[email protected]620f5712009-08-04 22:43:12140int ProxyScriptFetcherImpl::Fetch(const GURL& url,
141 std::string* bytes,
142 CompletionCallback* callback) {
[email protected]677c90572008-12-10 09:03:15143 // It is invalid to call Fetch() while a request is already in progress.
144 DCHECK(!cur_request_.get());
145
146 DCHECK(callback);
147 DCHECK(bytes);
148
149 cur_request_.reset(new URLRequest(url, this));
150 cur_request_->set_context(url_request_context_);
151 cur_request_->set_method("GET");
152
153 // Make sure that the PAC script is downloaded using a direct connection,
154 // to avoid circular dependencies (fetching is a part of proxy resolution).
[email protected]e0ef2c22009-06-03 23:54:44155 // Also disable the use of the disk cache. The cache is disabled so that if
156 // the user switches networks we don't potentially use the cached response
157 // from old network when we should in fact be re-fetching on the new network.
158 cur_request_->set_load_flags(LOAD_BYPASS_PROXY | LOAD_DISABLE_CACHE);
[email protected]677c90572008-12-10 09:03:15159
160 // Save the caller's info for notification on completion.
161 callback_ = callback;
162 result_bytes_ = bytes;
163 result_bytes_->clear();
164
165 // Post a task to timeout this request if it takes too long.
166 cur_request_id_ = ++next_id_;
167 MessageLoop::current()->PostDelayedTask(FROM_HERE,
168 task_factory_.NewRunnableMethod(&ProxyScriptFetcherImpl::OnTimeout,
169 cur_request_id_),
170 static_cast<int>(max_duration_ms));
171
172 // Start the request.
173 cur_request_->Start();
[email protected]620f5712009-08-04 22:43:12174 return ERR_IO_PENDING;
[email protected]677c90572008-12-10 09:03:15175}
176
177void ProxyScriptFetcherImpl::Cancel() {
178 // ResetCurRequestState will free the URLRequest, which will cause
179 // cancellation.
180 ResetCurRequestState();
181}
182
183void ProxyScriptFetcherImpl::OnAuthRequired(URLRequest* request,
184 AuthChallengeInfo* auth_info) {
185 DCHECK(request == cur_request_.get());
186 // TODO(eroman):
[email protected]13a279e2009-04-13 17:32:37187 LOG(WARNING) << "Auth required to fetch PAC script, aborting.";
[email protected]677c90572008-12-10 09:03:15188 result_code_ = ERR_NOT_IMPLEMENTED;
189 request->CancelAuth();
190}
191
192void ProxyScriptFetcherImpl::OnSSLCertificateError(URLRequest* request,
193 int cert_error,
194 X509Certificate* cert) {
195 DCHECK(request == cur_request_.get());
[email protected]13a279e2009-04-13 17:32:37196 LOG(WARNING) << "SSL certificate error when fetching PAC script, aborting.";
[email protected]677c90572008-12-10 09:03:15197 // Certificate errors are in same space as net errors.
198 result_code_ = cert_error;
199 request->Cancel();
200}
201
[email protected]677c90572008-12-10 09:03:15202void ProxyScriptFetcherImpl::OnResponseStarted(URLRequest* request) {
203 DCHECK(request == cur_request_.get());
204
205 if (!request->status().is_success()) {
206 OnResponseCompleted(request);
207 return;
208 }
209
210 // Require HTTP responses to have a success status code.
211 if (request->url().SchemeIs("http") || request->url().SchemeIs("https")) {
[email protected]f0a51fb52009-03-05 12:46:38212 // NOTE about status codes: We are like Firefox 3 in this respect.
[email protected]677c90572008-12-10 09:03:15213 // {IE 7, Safari 3, Opera 9.5} do not care about the status code.
214 if (request->GetResponseCode() != 200) {
[email protected]e0ef2c22009-06-03 23:54:44215 LOG(INFO) << "Fetched PAC script had (bad) status line: "
216 << request->response_headers()->GetStatusLine();
[email protected]677c90572008-12-10 09:03:15217 result_code_ = ERR_PAC_STATUS_NOT_OK;
218 request->Cancel();
219 return;
220 }
[email protected]13a279e2009-04-13 17:32:37221
222 // NOTE about mime types: We do not enforce mime types on PAC files.
223 // This is for compatibility with {IE 7, Firefox 3, Opera 9.5}. We will
224 // however log mismatches to help with debugging.
[email protected]e0ef2c22009-06-03 23:54:44225 std::string mime_type;
226 cur_request_->GetMimeType(&mime_type);
227 if (!IsPacMimeType(mime_type)) {
228 LOG(INFO) << "Fetched PAC script does not have a proper mime type: "
229 << mime_type;
[email protected]13a279e2009-04-13 17:32:37230 }
[email protected]677c90572008-12-10 09:03:15231 }
232
233 ReadBody(request);
234}
235
236void ProxyScriptFetcherImpl::OnReadCompleted(URLRequest* request,
237 int num_bytes) {
238 DCHECK(request == cur_request_.get());
239 if (num_bytes > 0) {
240 // Enforce maximum size bound.
241 if (num_bytes + result_bytes_->size() >
242 static_cast<size_t>(max_response_bytes)) {
243 result_code_ = ERR_FILE_TOO_BIG;
244 request->Cancel();
245 return;
246 }
[email protected]9dea9e1f2009-01-29 00:30:47247 result_bytes_->append(buf_->data(), num_bytes);
[email protected]677c90572008-12-10 09:03:15248 ReadBody(request);
249 } else { // Error while reading, or EOF
250 OnResponseCompleted(request);
251 }
252}
253
254void ProxyScriptFetcherImpl::OnResponseCompleted(URLRequest* request) {
255 DCHECK(request == cur_request_.get());
256
257 // Use |result_code_| as the request's error if we have already set it to
258 // something specific.
259 if (result_code_ == OK && !request->status().is_success())
260 result_code_ = request->status().os_error();
261
262 FetchCompleted();
263}
264
265void ProxyScriptFetcherImpl::ReadBody(URLRequest* request) {
266 int num_bytes;
267 if (request->Read(buf_, kBufSize, &num_bytes)) {
268 OnReadCompleted(request, num_bytes);
269 } else if (!request->status().is_io_pending()) {
270 // Read failed synchronously.
271 OnResponseCompleted(request);
272 }
273}
274
275void ProxyScriptFetcherImpl::FetchCompleted() {
276 // On error, the caller expects empty string for bytes.
277 if (result_code_ != OK)
278 result_bytes_->clear();
279
280 int result_code = result_code_;
281 CompletionCallback* callback = callback_;
282
283 ResetCurRequestState();
284
285 callback->Run(result_code);
286}
287
288void ProxyScriptFetcherImpl::ResetCurRequestState() {
289 cur_request_.reset();
290 cur_request_id_ = 0;
291 callback_ = NULL;
292 result_code_ = OK;
293 result_bytes_ = NULL;
294}
295
296void ProxyScriptFetcherImpl::OnTimeout(int id) {
297 // Timeout tasks may outlive the URLRequest they reference. Make sure it
298 // is still applicable.
299 if (cur_request_id_ != id)
300 return;
301
302 DCHECK(cur_request_.get());
303 result_code_ = ERR_TIMED_OUT;
304 cur_request_->Cancel();
305}
306
307// static
308ProxyScriptFetcher* ProxyScriptFetcher::Create(
309 URLRequestContext* url_request_context) {
310 return new ProxyScriptFetcherImpl(url_request_context);
311}
312
313// static
314int ProxyScriptFetcher::SetTimeoutConstraintForUnittest(
315 int timeout_ms) {
316 int prev = max_duration_ms;
317 max_duration_ms = timeout_ms;
318 return prev;
319}
320
321// static
322size_t ProxyScriptFetcher::SetSizeConstraintForUnittest(size_t size_bytes) {
323 size_t prev = max_response_bytes;
324 max_response_bytes = size_bytes;
325 return prev;
326}
327
328} // namespace net