| Index: net/proxy/proxy_script_fetcher.cc
|
| ===================================================================
|
| --- net/proxy/proxy_script_fetcher.cc (revision 0)
|
| +++ net/proxy/proxy_script_fetcher.cc (revision 0)
|
| @@ -0,0 +1,301 @@
|
| +// Copyright (c) 2008 The Chromium 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 "net/proxy/proxy_script_fetcher.h"
|
| +
|
| +#include "base/compiler_specific.h"
|
| +#include "base/message_loop.h"
|
| +#include "base/string_util.h"
|
| +#include "net/base/load_flags.h"
|
| +#include "net/url_request/url_request.h"
|
| +
|
| +// TODO(eroman):
|
| +// - Support auth-prompts.
|
| +
|
| +namespace net {
|
| +
|
| +namespace {
|
| +
|
| +// The maximum size (in bytes) allowed for a PAC script. Responses exceeding
|
| +// this will fail with ERR_FILE_TOO_BIG.
|
| +int max_response_bytes = 1048576; // 1 megabyte
|
| +
|
| +// The maximum duration (in milliseconds) allowed for fetching the PAC script.
|
| +// Responses exceeding this will fail with ERR_TIMED_OUT.
|
| +int max_duration_ms = 300000; // 5 minutes
|
| +
|
| +} // namespace
|
| +
|
| +class ProxyScriptFetcherImpl : public ProxyScriptFetcher,
|
| + public URLRequest::Delegate {
|
| + public:
|
| + // Creates a ProxyScriptFetcher that issues requests through
|
| + // |url_request_context|. |url_request_context| must remain valid for the
|
| + // lifetime of ProxyScriptFetcherImpl.
|
| + explicit ProxyScriptFetcherImpl(URLRequestContext* url_request_context);
|
| +
|
| + virtual ~ProxyScriptFetcherImpl();
|
| +
|
| + // ProxyScriptFetcher methods:
|
| +
|
| + virtual void Fetch(const GURL& url, std::string* bytes,
|
| + CompletionCallback* callback);
|
| + virtual void Cancel();
|
| +
|
| + // URLRequest::Delegate methods:
|
| +
|
| + virtual void OnAuthRequired(URLRequest* request,
|
| + AuthChallengeInfo* auth_info);
|
| + virtual void OnSSLCertificateError(URLRequest* request, int cert_error,
|
| + X509Certificate* cert);
|
| + virtual void OnReceivedRedirect(URLRequest* request, const GURL& to_url);
|
| + virtual void OnResponseStarted(URLRequest* request);
|
| + virtual void OnReadCompleted(URLRequest* request, int num_bytes);
|
| + virtual void OnResponseCompleted(URLRequest* request);
|
| +
|
| + private:
|
| + // Read more bytes from the response.
|
| + void ReadBody(URLRequest* request);
|
| +
|
| + // Called once the request has completed to notify the caller of
|
| + // |response_code_| and |response_bytes_|.
|
| + void FetchCompleted();
|
| +
|
| + // Clear out the state for the current request.
|
| + void ResetCurRequestState();
|
| +
|
| + // Callback for time-out task of request with id |id|.
|
| + void OnTimeout(int id);
|
| +
|
| + // Factory for creating the time-out task. This takes care of revoking
|
| + // outstanding tasks when |this| is deleted.
|
| + ScopedRunnableMethodFactory<ProxyScriptFetcherImpl> task_factory_;
|
| +
|
| + // The context used for making network requests.
|
| + URLRequestContext* url_request_context_;
|
| +
|
| + // Buffer that URLRequest writes into.
|
| + enum { kBufSize = 4096 };
|
| + char buf_[kBufSize];
|
| +
|
| + // The next ID to use for |cur_request_| (monotonically increasing).
|
| + int next_id_;
|
| +
|
| + // The current (in progress) request, or NULL.
|
| + scoped_ptr<URLRequest> cur_request_;
|
| +
|
| + // State for current request (only valid when |cur_request_| is not NULL):
|
| +
|
| + // Unique ID for the current request.
|
| + int cur_request_id_;
|
| +
|
| + // Callback to invoke on completion of the fetch.
|
| + CompletionCallback* callback_;
|
| +
|
| + // Holds the error condition that was hit on the current request, or OK.
|
| + int result_code_;
|
| +
|
| + // Holds the bytes read so far. Will not exceed |max_response_bytes|. This
|
| + // buffer is owned by the owner of |callback|.
|
| + std::string* result_bytes_;
|
| +};
|
| +
|
| +ProxyScriptFetcherImpl::ProxyScriptFetcherImpl(
|
| + URLRequestContext* url_request_context)
|
| + : ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
|
| + url_request_context_(url_request_context),
|
| + next_id_(0),
|
| + cur_request_(NULL),
|
| + cur_request_id_(0),
|
| + callback_(NULL),
|
| + result_code_(OK),
|
| + result_bytes_(NULL) {
|
| + DCHECK(url_request_context);
|
| +}
|
| +
|
| +ProxyScriptFetcherImpl::~ProxyScriptFetcherImpl() {
|
| + // The URLRequest's destructor will cancel the outstanding request, and
|
| + // ensure that the delegate (this) is not called again.
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::Fetch(const GURL& url,
|
| + std::string* bytes,
|
| + CompletionCallback* callback) {
|
| + // It is invalid to call Fetch() while a request is already in progress.
|
| + DCHECK(!cur_request_.get());
|
| +
|
| + DCHECK(callback);
|
| + DCHECK(bytes);
|
| +
|
| + cur_request_.reset(new URLRequest(url, this));
|
| + cur_request_->set_context(url_request_context_);
|
| + cur_request_->set_method("GET");
|
| +
|
| + // Make sure that the PAC script is downloaded using a direct connection,
|
| + // to avoid circular dependencies (fetching is a part of proxy resolution).
|
| + cur_request_->set_load_flags(LOAD_BYPASS_PROXY);
|
| +
|
| + // Save the caller's info for notification on completion.
|
| + callback_ = callback;
|
| + result_bytes_ = bytes;
|
| + result_bytes_->clear();
|
| +
|
| + // Post a task to timeout this request if it takes too long.
|
| + cur_request_id_ = ++next_id_;
|
| + MessageLoop::current()->PostDelayedTask(FROM_HERE,
|
| + task_factory_.NewRunnableMethod(&ProxyScriptFetcherImpl::OnTimeout,
|
| + cur_request_id_),
|
| + static_cast<int>(max_duration_ms));
|
| +
|
| + // Start the request.
|
| + cur_request_->Start();
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::Cancel() {
|
| + // ResetCurRequestState will free the URLRequest, which will cause
|
| + // cancellation.
|
| + ResetCurRequestState();
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::OnAuthRequired(URLRequest* request,
|
| + AuthChallengeInfo* auth_info) {
|
| + DCHECK(request == cur_request_.get());
|
| + // TODO(eroman):
|
| + result_code_ = ERR_NOT_IMPLEMENTED;
|
| + request->CancelAuth();
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::OnSSLCertificateError(URLRequest* request,
|
| + int cert_error,
|
| + X509Certificate* cert) {
|
| + DCHECK(request == cur_request_.get());
|
| + // Certificate errors are in same space as net errors.
|
| + result_code_ = cert_error;
|
| + request->Cancel();
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::OnReceivedRedirect(URLRequest* request,
|
| + const GURL& to_url) {
|
| + DCHECK(request == cur_request_.get());
|
| + // OK, thanks for telling.
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::OnResponseStarted(URLRequest* request) {
|
| + DCHECK(request == cur_request_.get());
|
| +
|
| + if (!request->status().is_success()) {
|
| + OnResponseCompleted(request);
|
| + return;
|
| + }
|
| +
|
| + // Require HTTP responses to have a success status code.
|
| + if (request->url().SchemeIs("http") || request->url().SchemeIs("https")) {
|
| + // NOTE about mime types: We do not enforce mime types on PAC files.
|
| + // This is for compatibility with {IE 7, Firefox 3, Opera 9.5}
|
| +
|
| + // NOTE about status codes: We are like Firefox 3 in this respect.
|
| + // {IE 7, Safari 3, Opera 9.5} do not care about the status code.
|
| + if (request->GetResponseCode() != 200) {
|
| + result_code_ = ERR_PAC_STATUS_NOT_OK;
|
| + request->Cancel();
|
| + return;
|
| + }
|
| + }
|
| +
|
| + ReadBody(request);
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::OnReadCompleted(URLRequest* request,
|
| + int num_bytes) {
|
| + DCHECK(request == cur_request_.get());
|
| + if (num_bytes > 0) {
|
| + // Enforce maximum size bound.
|
| + if (num_bytes + result_bytes_->size() >
|
| + static_cast<size_t>(max_response_bytes)) {
|
| + result_code_ = ERR_FILE_TOO_BIG;
|
| + request->Cancel();
|
| + return;
|
| + }
|
| + result_bytes_->append(buf_, num_bytes);
|
| + ReadBody(request);
|
| + } else { // Error while reading, or EOF
|
| + OnResponseCompleted(request);
|
| + }
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::OnResponseCompleted(URLRequest* request) {
|
| + DCHECK(request == cur_request_.get());
|
| +
|
| + // Use |result_code_| as the request's error if we have already set it to
|
| + // something specific.
|
| + if (result_code_ == OK && !request->status().is_success())
|
| + result_code_ = request->status().os_error();
|
| +
|
| + FetchCompleted();
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::ReadBody(URLRequest* request) {
|
| + int num_bytes;
|
| + if (request->Read(buf_, kBufSize, &num_bytes)) {
|
| + OnReadCompleted(request, num_bytes);
|
| + } else if (!request->status().is_io_pending()) {
|
| + // Read failed synchronously.
|
| + OnResponseCompleted(request);
|
| + }
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::FetchCompleted() {
|
| + // On error, the caller expects empty string for bytes.
|
| + if (result_code_ != OK)
|
| + result_bytes_->clear();
|
| +
|
| + int result_code = result_code_;
|
| + CompletionCallback* callback = callback_;
|
| +
|
| + ResetCurRequestState();
|
| +
|
| + callback->Run(result_code);
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::ResetCurRequestState() {
|
| + cur_request_.reset();
|
| + cur_request_id_ = 0;
|
| + callback_ = NULL;
|
| + result_code_ = OK;
|
| + result_bytes_ = NULL;
|
| +}
|
| +
|
| +void ProxyScriptFetcherImpl::OnTimeout(int id) {
|
| + // Timeout tasks may outlive the URLRequest they reference. Make sure it
|
| + // is still applicable.
|
| + if (cur_request_id_ != id)
|
| + return;
|
| +
|
| + DCHECK(cur_request_.get());
|
| + result_code_ = ERR_TIMED_OUT;
|
| + cur_request_->Cancel();
|
| +}
|
| +
|
| +// static
|
| +ProxyScriptFetcher* ProxyScriptFetcher::Create(
|
| + URLRequestContext* url_request_context) {
|
| + return new ProxyScriptFetcherImpl(url_request_context);
|
| +}
|
| +
|
| +// static
|
| +int ProxyScriptFetcher::SetTimeoutConstraintForUnittest(
|
| + int timeout_ms) {
|
| + int prev = max_duration_ms;
|
| + max_duration_ms = timeout_ms;
|
| + return prev;
|
| +}
|
| +
|
| +// static
|
| +size_t ProxyScriptFetcher::SetSizeConstraintForUnittest(size_t size_bytes) {
|
| + size_t prev = max_response_bytes;
|
| + max_response_bytes = size_bytes;
|
| + return prev;
|
| +}
|
| +
|
| +} // namespace net
|
|
|
| Property changes on: net\proxy\proxy_script_fetcher.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|