summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/http/http_network_transaction_spdy2_unittest.cc7
-rw-r--r--net/http/http_network_transaction_spdy3_unittest.cc7
-rw-r--r--net/http/http_stream_factory_impl.cc9
-rw-r--r--net/http/http_stream_factory_impl_job.cc13
-rw-r--r--net/http/http_util_icu.cc5
-rw-r--r--net/socket/client_socket_pool_manager.cc6
-rw-r--r--net/url_request/url_request_ftp_job.cc207
-rw-r--r--net/url_request/url_request_ftp_job.h24
-rw-r--r--net/url_request/url_request_ftp_job_unittest.cc378
-rw-r--r--net/url_request/url_request_test_util.cc27
-rw-r--r--net/url_request/url_request_test_util.h10
11 files changed, 587 insertions, 106 deletions
diff --git a/net/http/http_network_transaction_spdy2_unittest.cc b/net/http/http_network_transaction_spdy2_unittest.cc
index 89fa251..2ea9bf7 100644
--- a/net/http/http_network_transaction_spdy2_unittest.cc
+++ b/net/http/http_network_transaction_spdy2_unittest.cc
@@ -6226,6 +6226,13 @@ TEST_F(HttpNetworkTransactionSpdy2Test, GroupNameForHTTPProxyConnections) {
"ssl/host.with.alternate:443",
true,
},
+
+ {
+ "http_proxy",
+ "ftp://ftp.google.com/http_proxy_normal",
+ "ftp/ftp.google.com:21",
+ false,
+ },
};
HttpStreamFactory::set_use_alternate_protocols(true);
diff --git a/net/http/http_network_transaction_spdy3_unittest.cc b/net/http/http_network_transaction_spdy3_unittest.cc
index 5a8155b..f2ee049 100644
--- a/net/http/http_network_transaction_spdy3_unittest.cc
+++ b/net/http/http_network_transaction_spdy3_unittest.cc
@@ -6226,6 +6226,13 @@ TEST_F(HttpNetworkTransactionSpdy3Test, GroupNameForHTTPProxyConnections) {
"ssl/host.with.alternate:443",
true,
},
+
+ {
+ "http_proxy",
+ "ftp://ftp.google.com/http_proxy_normal",
+ "ftp/ftp.google.com:21",
+ false,
+ },
};
HttpStreamFactory::set_use_alternate_protocols(true);
diff --git a/net/http/http_stream_factory_impl.cc b/net/http/http_stream_factory_impl.cc
index 5896a93..24ae8a6 100644
--- a/net/http/http_stream_factory_impl.cc
+++ b/net/http/http_stream_factory_impl.cc
@@ -70,6 +70,9 @@ HttpStreamRequest* HttpStreamFactoryImpl::RequestStream(
GetAlternateProtocolRequestFor(request_info.url, &alternate_url);
Job* alternate_job = NULL;
if (has_alternate_protocol) {
+ // Never share connection with other jobs for FTP requests.
+ DCHECK(!request_info.url.SchemeIs("ftp"));
+
HttpRequestInfo alternate_request_info = request_info;
alternate_request_info.url = alternate_url;
alternate_job =
@@ -83,6 +86,9 @@ HttpStreamRequest* HttpStreamFactoryImpl::RequestStream(
proxy_ssl_config, net_log.net_log());
request->AttachJob(job);
if (alternate_job) {
+ // Never share connection with other jobs for FTP requests.
+ DCHECK(!request_info.url.SchemeIs("ftp"));
+
job->WaitFor(alternate_job);
// Make sure to wait until we call WaitFor(), before starting
// |alternate_job|, otherwise |alternate_job| will not notify |job|
@@ -136,6 +142,9 @@ bool HttpStreamFactoryImpl::GetAlternateProtocolRequestFor(
if (!use_alternate_protocols())
return false;
+ if (original_url.SchemeIs("ftp"))
+ return false;
+
HostPortPair origin = HostPortPair(original_url.HostNoBrackets(),
original_url.EffectiveIntPort());
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index 9f6acfb..034bb949 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -573,7 +573,14 @@ int HttpStreamFactoryImpl::Job::DoStart() {
&request_info_.url, &origin_url_));
// Don't connect to restricted ports.
- if (!IsPortAllowedByDefault(port) && !IsPortAllowedByOverride(port)) {
+ bool is_port_allowed = IsPortAllowedByDefault(port);
+ if (request_info_.url.SchemeIs("ftp")) {
+ // Never share connection with other jobs for FTP requests.
+ DCHECK(!waiting_job_);
+
+ is_port_allowed = IsPortAllowedByFtp(port);
+ }
+ if (!is_port_allowed && !IsPortAllowedByOverride(port)) {
if (waiting_job_) {
waiting_job_->Resume(this);
waiting_job_ = NULL;
@@ -938,8 +945,10 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() {
}
if (!using_spdy_) {
+ // We may get ftp scheme when fetching ftp resources through proxy.
bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) &&
- request_info_.url.SchemeIs("http");
+ (request_info_.url.SchemeIs("http") ||
+ request_info_.url.SchemeIs("ftp"));
if (stream_factory_->http_pipelined_host_pool_.
IsExistingPipelineAvailableForKey(*http_pipelining_key_.get())) {
stream_.reset(stream_factory_->http_pipelined_host_pool_.
diff --git a/net/http/http_util_icu.cc b/net/http/http_util_icu.cc
index 8000acc..64e7424 100644
--- a/net/http/http_util_icu.cc
+++ b/net/http/http_util_icu.cc
@@ -22,7 +22,10 @@ std::string HttpUtil::PathForRequest(const GURL& url) {
// static
std::string HttpUtil::SpecForRequest(const GURL& url) {
- DCHECK(url.is_valid() && (url.SchemeIs("http") || url.SchemeIs("https")));
+ // We may get ftp scheme when fetching ftp resources through proxy.
+ DCHECK(url.is_valid() && (url.SchemeIs("http") ||
+ url.SchemeIs("https") ||
+ url.SchemeIs("ftp")));
return SimplifyUrlForRequest(url).spec();
}
diff --git a/net/socket/client_socket_pool_manager.cc b/net/socket/client_socket_pool_manager.cc
index 5884e08..71b9d6d 100644
--- a/net/socket/client_socket_pool_manager.cc
+++ b/net/socket/client_socket_pool_manager.cc
@@ -112,6 +112,12 @@ int InitSocketPoolHelper(const GURL& request_url,
// Determine the host and port to connect to.
std::string connection_group = origin_host_port.ToString();
DCHECK(!connection_group.empty());
+ if (request_url.SchemeIs("ftp")) {
+ // Combining FTP with forced SPDY over SSL would be a "path to madness".
+ // Make sure we never do that.
+ DCHECK(!using_ssl);
+ connection_group = "ftp/" + connection_group;
+ }
if (using_ssl) {
// All connections in a group should use the same SSLConfig settings.
// Encode version_max in the connection group's name, unless it's the
diff --git a/net/url_request/url_request_ftp_job.cc b/net/url_request/url_request_ftp_job.cc
index 27021a3..343c4d7 100644
--- a/net/url_request/url_request_ftp_job.cc
+++ b/net/url_request/url_request_ftp_job.cc
@@ -9,10 +9,13 @@
#include "base/utf_string_conversions.h"
#include "net/base/auth.h"
#include "net/base/host_port_pair.h"
+#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/ftp/ftp_response_info.h"
#include "net/ftp/ftp_transaction_factory.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_transaction_factory.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_error_job.h"
@@ -25,6 +28,8 @@ URLRequestFtpJob::URLRequestFtpJob(
FtpTransactionFactory* ftp_transaction_factory,
FtpAuthCache* ftp_auth_cache)
: URLRequestJob(request, network_delegate),
+ pac_request_(NULL),
+ response_info_(NULL),
read_in_progress_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
ftp_transaction_factory_(ftp_transaction_factory),
@@ -53,37 +58,85 @@ URLRequestJob* URLRequestFtpJob::Factory(URLRequest* request,
request->context()->ftp_auth_cache());
}
+bool URLRequestFtpJob::IsSafeRedirect(const GURL& location) {
+ // Disallow all redirects.
+ return false;
+}
+
bool URLRequestFtpJob::GetMimeType(std::string* mime_type) const {
- if (transaction_->GetResponseInfo()->is_directory_listing) {
- *mime_type = "text/vnd.chromium.ftp-dir";
- return true;
+ if (proxy_info_.is_direct()) {
+ if (ftp_transaction_->GetResponseInfo()->is_directory_listing) {
+ *mime_type = "text/vnd.chromium.ftp-dir";
+ return true;
+ }
+ } else {
+ // No special handling of MIME type is needed. As opposed to direct FTP
+ // transaction, we do not get a raw directory listing to parse.
+ return http_transaction_->GetResponseInfo()->
+ headers->GetMimeType(mime_type);
}
return false;
}
+void URLRequestFtpJob::GetResponseInfo(HttpResponseInfo* info) {
+ if (response_info_)
+ *info = *response_info_;
+}
+
HostPortPair URLRequestFtpJob::GetSocketAddress() const {
- if (!transaction_.get()) {
- return HostPortPair();
+ if (proxy_info_.is_direct()) {
+ if (!ftp_transaction_)
+ return HostPortPair();
+ return ftp_transaction_->GetResponseInfo()->socket_address;
+ } else {
+ if (!http_transaction_)
+ return HostPortPair();
+ return http_transaction_->GetResponseInfo()->socket_address;
}
- return transaction_->GetResponseInfo()->socket_address;
}
URLRequestFtpJob::~URLRequestFtpJob() {
+ if (pac_request_)
+ request_->context()->proxy_service()->CancelPacRequest(pac_request_);
+}
+
+void URLRequestFtpJob::OnResolveProxyComplete(int result) {
+ pac_request_ = NULL;
+
+ if (result != OK) {
+ OnStartCompletedAsync(result);
+ return;
+ }
+
+ // Remove unsupported proxies from the list.
+ proxy_info_.RemoveProxiesWithoutScheme(
+ ProxyServer::SCHEME_DIRECT |
+ ProxyServer::SCHEME_HTTP |
+ ProxyServer::SCHEME_HTTPS);
+
+ // TODO(phajdan.jr): Implement proxy fallback, http://crbug.com/171495 .
+ if (proxy_info_.is_direct())
+ StartFtpTransaction();
+ else if (proxy_info_.is_http() || proxy_info_.is_https())
+ StartHttpTransaction();
+ else
+ OnStartCompletedAsync(ERR_NO_SUPPORTED_PROXIES);
}
-void URLRequestFtpJob::StartTransaction() {
+void URLRequestFtpJob::StartFtpTransaction() {
// Create a transaction.
- DCHECK(!transaction_.get());
+ DCHECK(!ftp_transaction_);
- transaction_.reset(ftp_transaction_factory_->CreateTransaction());
+ ftp_request_info_.url = request_->url();
+ ftp_transaction_.reset(ftp_transaction_factory_->CreateTransaction());
// No matter what, we want to report our status as IO pending since we will
// be notifying our consumer asynchronously via OnStartCompleted.
SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
int rv;
- if (transaction_.get()) {
- rv = transaction_->Start(
- &request_info_,
+ if (ftp_transaction_) {
+ rv = ftp_transaction_->Start(
+ &ftp_request_info_,
base::Bind(&URLRequestFtpJob::OnStartCompleted,
base::Unretained(this)),
request_->net_log());
@@ -94,28 +147,61 @@ void URLRequestFtpJob::StartTransaction() {
}
// The transaction started synchronously, but we need to notify the
// URLRequest delegate via the message loop.
- MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&URLRequestFtpJob::OnStartCompleted,
- weak_factory_.GetWeakPtr(), rv));
+ OnStartCompletedAsync(rv);
+}
+
+void URLRequestFtpJob::StartHttpTransaction() {
+ // Create a transaction.
+ DCHECK(!http_transaction_);
+
+ // Do not cache FTP responses sent through HTTP proxy.
+ // Do not send HTTP auth data because this is really FTP.
+ request_->set_load_flags(request_->load_flags() |
+ LOAD_DISABLE_CACHE |
+ LOAD_DO_NOT_SAVE_COOKIES |
+ LOAD_DO_NOT_SEND_AUTH_DATA |
+ LOAD_DO_NOT_SEND_COOKIES);
+
+ http_request_info_.url = request_->url();
+ http_request_info_.method = request_->method();
+ http_request_info_.load_flags = request_->load_flags();
+ http_request_info_.priority = request_->priority();
+ http_request_info_.request_id = request_->identifier();
+
+ int rv = request_->context()->http_transaction_factory()->CreateTransaction(
+ &http_transaction_, NULL);
+ if (rv == OK) {
+ rv = http_transaction_->Start(
+ &http_request_info_,
+ base::Bind(&URLRequestFtpJob::OnStartCompleted,
+ base::Unretained(this)),
+ request_->net_log());
+ if (rv == ERR_IO_PENDING)
+ return;
+ }
+ // The transaction started synchronously, but we need to notify the
+ // URLRequest delegate via the message loop.
+ OnStartCompletedAsync(rv);
}
void URLRequestFtpJob::OnStartCompleted(int result) {
// Clear the IO_PENDING status
SetStatus(URLRequestStatus());
- // Note that transaction_ may be NULL due to a creation failure.
- if (transaction_.get()) {
+ // Note that ftp_transaction_ may be NULL due to a creation failure.
+ if (ftp_transaction_) {
// FTP obviously doesn't have HTTP Content-Length header. We have to pass
// the content size information manually.
set_expected_content_size(
- transaction_->GetResponseInfo()->expected_content_size);
+ ftp_transaction_->GetResponseInfo()->expected_content_size);
}
if (result == OK) {
+ if (http_transaction_)
+ response_info_ = http_transaction_->GetResponseInfo();
NotifyHeadersComplete();
- } else if (transaction_.get() &&
- transaction_->GetResponseInfo()->needs_auth) {
+ } else if (ftp_transaction_ &&
+ ftp_transaction_->GetResponseInfo()->needs_auth) {
GURL origin = request_->url().GetOrigin();
if (server_auth_ && server_auth_->state == AUTH_STATE_HAVE_AUTH) {
ftp_auth_cache_->Remove(origin, server_auth_->credentials);
@@ -137,6 +223,13 @@ void URLRequestFtpJob::OnStartCompleted(int result) {
}
}
+void URLRequestFtpJob::OnStartCompletedAsync(int result) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&URLRequestFtpJob::OnStartCompleted,
+ weak_factory_.GetWeakPtr(), result));
+}
+
void URLRequestFtpJob::OnReadCompleted(int result) {
read_in_progress_ = false;
if (result == 0) {
@@ -151,45 +244,70 @@ void URLRequestFtpJob::OnReadCompleted(int result) {
}
void URLRequestFtpJob::RestartTransactionWithAuth() {
+ DCHECK(ftp_transaction_);
DCHECK(server_auth_ && server_auth_->state == AUTH_STATE_HAVE_AUTH);
// No matter what, we want to report our status as IO pending since we will
// be notifying our consumer asynchronously via OnStartCompleted.
SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
- int rv = transaction_->RestartWithAuth(
+ int rv = ftp_transaction_->RestartWithAuth(
server_auth_->credentials,
base::Bind(&URLRequestFtpJob::OnStartCompleted,
base::Unretained(this)));
if (rv == ERR_IO_PENDING)
return;
- MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&URLRequestFtpJob::OnStartCompleted,
- weak_factory_.GetWeakPtr(), rv));
+ OnStartCompletedAsync(rv);
}
void URLRequestFtpJob::Start() {
- DCHECK(!transaction_.get());
- request_info_.url = request_->url();
- StartTransaction();
+ DCHECK(!pac_request_);
+ DCHECK(!ftp_transaction_);
+ DCHECK(!http_transaction_);
+
+ int rv = OK;
+ if (request_->load_flags() & LOAD_BYPASS_PROXY) {
+ proxy_info_.UseDirect();
+ } else {
+ rv = request_->context()->proxy_service()->ResolveProxy(
+ request_->url(),
+ &proxy_info_,
+ base::Bind(&URLRequestFtpJob::OnResolveProxyComplete,
+ base::Unretained(this)),
+ &pac_request_,
+ request_->net_log());
+
+ if (rv == ERR_IO_PENDING)
+ return;
+ }
+ OnResolveProxyComplete(rv);
}
void URLRequestFtpJob::Kill() {
- if (!transaction_.get())
- return;
- transaction_.reset();
+ if (ftp_transaction_)
+ ftp_transaction_.reset();
+ if (http_transaction_)
+ http_transaction_.reset();
URLRequestJob::Kill();
weak_factory_.InvalidateWeakPtrs();
}
LoadState URLRequestFtpJob::GetLoadState() const {
- return transaction_.get() ?
- transaction_->GetLoadState() : LOAD_STATE_IDLE;
+ if (proxy_info_.is_direct()) {
+ return ftp_transaction_ ?
+ ftp_transaction_->GetLoadState() : LOAD_STATE_IDLE;
+ } else {
+ return http_transaction_ ?
+ http_transaction_->GetLoadState() : LOAD_STATE_IDLE;
+ }
}
bool URLRequestFtpJob::NeedsAuth() {
+ // TODO(phajdan.jr): Implement proxy auth, http://crbug.com/171497 .
+ if (!ftp_transaction_)
+ return false;
+
// Note that we only have to worry about cases where an actual FTP server
// requires auth (and not a proxy), because connecting to FTP via proxy
// effectively means the browser communicates via HTTP, and uses HTTP's
@@ -211,6 +329,7 @@ void URLRequestFtpJob::GetAuthChallengeInfo(
}
void URLRequestFtpJob::SetAuth(const AuthCredentials& credentials) {
+ DCHECK(ftp_transaction_);
DCHECK(NeedsAuth());
server_auth_->state = AUTH_STATE_HAVE_AUTH;
server_auth_->credentials = credentials;
@@ -221,16 +340,14 @@ void URLRequestFtpJob::SetAuth(const AuthCredentials& credentials) {
}
void URLRequestFtpJob::CancelAuth() {
+ DCHECK(ftp_transaction_);
DCHECK(NeedsAuth());
server_auth_->state = AUTH_STATE_CANCELED;
// Once the auth is cancelled, we proceed with the request as though
// there were no auth. Schedule this for later so that we don't cause
// any recursing into the caller as a result of this call.
- MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&URLRequestFtpJob::OnStartCompleted,
- weak_factory_.GetWeakPtr(), OK));
+ OnStartCompletedAsync(OK);
}
UploadProgress URLRequestFtpJob::GetUploadProgress() const {
@@ -244,9 +361,17 @@ bool URLRequestFtpJob::ReadRawData(IOBuffer* buf,
DCHECK(bytes_read);
DCHECK(!read_in_progress_);
- int rv = transaction_->Read(buf, buf_size,
- base::Bind(&URLRequestFtpJob::OnReadCompleted,
- base::Unretained(this)));
+ int rv;
+ if (proxy_info_.is_direct()) {
+ rv = ftp_transaction_->Read(buf, buf_size,
+ base::Bind(&URLRequestFtpJob::OnReadCompleted,
+ base::Unretained(this)));
+ } else {
+ rv = http_transaction_->Read(buf, buf_size,
+ base::Bind(&URLRequestFtpJob::OnReadCompleted,
+ base::Unretained(this)));
+ }
+
if (rv >= 0) {
*bytes_read = rv;
return true;
diff --git a/net/url_request/url_request_ftp_job.h b/net/url_request/url_request_ftp_job.h
index d05e3d8..3b053c1 100644
--- a/net/url_request/url_request_ftp_job.h
+++ b/net/url_request/url_request_ftp_job.h
@@ -9,9 +9,12 @@
#include "base/memory/weak_ptr.h"
#include "net/base/auth.h"
-#include "net/base/completion_callback.h"
#include "net/ftp/ftp_request_info.h"
#include "net/ftp/ftp_transaction.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_transaction.h"
+#include "net/proxy/proxy_info.h"
+#include "net/proxy/proxy_service.h"
#include "net/url_request/url_request_job.h"
namespace net {
@@ -35,15 +38,21 @@ class URLRequestFtpJob : public URLRequestJob {
const std::string& scheme);
// Overridden from URLRequestJob:
+ virtual bool IsSafeRedirect(const GURL& location) OVERRIDE;
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
+ virtual void GetResponseInfo(HttpResponseInfo* info) OVERRIDE;
virtual HostPortPair GetSocketAddress() const OVERRIDE;
private:
virtual ~URLRequestFtpJob();
- void StartTransaction();
+ void OnResolveProxyComplete(int result);
+
+ void StartFtpTransaction();
+ void StartHttpTransaction();
void OnStartCompleted(int result);
+ void OnStartCompletedAsync(int result);
void OnReadCompleted(int result);
void RestartTransactionWithAuth();
@@ -66,8 +75,15 @@ class URLRequestFtpJob : public URLRequestJob {
int buf_size,
int *bytes_read) OVERRIDE;
- FtpRequestInfo request_info_;
- scoped_ptr<FtpTransaction> transaction_;
+ ProxyInfo proxy_info_;
+ ProxyService::PacRequest* pac_request_;
+
+ FtpRequestInfo ftp_request_info_;
+ scoped_ptr<FtpTransaction> ftp_transaction_;
+
+ HttpRequestInfo http_request_info_;
+ scoped_ptr<HttpTransaction> http_transaction_;
+ const HttpResponseInfo* response_info_;
bool read_in_progress_;
diff --git a/net/url_request/url_request_ftp_job_unittest.cc b/net/url_request/url_request_ftp_job_unittest.cc
index e13a80d..8fda107 100644
--- a/net/url_request/url_request_ftp_job_unittest.cc
+++ b/net/url_request/url_request_ftp_job_unittest.cc
@@ -2,74 +2,358 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/ftp/ftp_auth_cache.h"
-#include "net/ftp/ftp_transaction.h"
-#include "net/ftp/ftp_transaction_factory.h"
-#include "net/url_request/ftp_protocol_handler.h"
+#include "base/run_loop.h"
+#include "net/proxy/proxy_config_service.h"
+#include "net/socket/socket_test_util.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_ftp_job.h"
#include "net/url_request/url_request_status.h"
-#include "testing/gmock/include/gmock/gmock.h"
+#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
-using ::testing::Return;
-using ::testing::_;
-
namespace net {
-class MockFtpTransactionFactory : public FtpTransactionFactory {
+namespace {
+
+class SimpleProxyConfigService : public ProxyConfigService {
public:
- MOCK_METHOD0(CreateTransaction, FtpTransaction*());
- MOCK_METHOD1(Suspend, void(bool suspend));
+ SimpleProxyConfigService() {
+ // Any FTP requests that ever go through HTTP paths are proxied requests.
+ config_.proxy_rules().ParseFromString("ftp=localhost");
+ }
+
+ virtual void AddObserver(Observer* observer) OVERRIDE {
+ observer_ = observer;
+ }
+
+ virtual void RemoveObserver(Observer* observer) OVERRIDE {
+ if (observer_ == observer) {
+ observer_ = NULL;
+ }
+ }
+
+ virtual ConfigAvailability GetLatestProxyConfig(
+ ProxyConfig* config) OVERRIDE {
+ *config = config_;
+ return CONFIG_VALID;
+ }
+
+ void IncrementConfigId() {
+ config_.set_id(config_.id() + 1);
+ observer_->OnProxyConfigChanged(config_, ProxyConfigService::CONFIG_VALID);
+ }
+
+ private:
+ ProxyConfig config_;
+ Observer* observer_;
};
-class MockURLRequestDelegate : public URLRequest::Delegate {
+class FtpTestURLRequestContext : public TestURLRequestContext {
public:
- MOCK_METHOD3(OnReceivedRedirect, void(URLRequest* request,
- const GURL& new_url,
- bool* defer_redirect));
- MOCK_METHOD2(OnAuthRequired, void(URLRequest* request,
- AuthChallengeInfo* auth_info));
- MOCK_METHOD2(OnCertificateRequested,
- void(URLRequest* request,
- SSLCertRequestInfo* cert_request_info));
- MOCK_METHOD3(OnSSLCertificateError, void(URLRequest* request,
- const SSLInfo& ssl_info,
- bool fatal));
- MOCK_METHOD1(OnResponseStarted, void(URLRequest* request));
- MOCK_METHOD2(OnReadCompleted, void(URLRequest* request, int bytes_read));
+ FtpTestURLRequestContext(ClientSocketFactory* socket_factory,
+ ProxyService* proxy_service,
+ NetworkDelegate* network_delegate)
+ : TestURLRequestContext(true) {
+ set_client_socket_factory(socket_factory);
+ context_storage_.set_proxy_service(proxy_service);
+ set_network_delegate(network_delegate);
+ Init();
+ }
};
-ACTION_P(HandleOnResponseStarted, expected_status) {
- EXPECT_EQ(expected_status, arg0->status().status());
+class URLRequestFtpJobTest : public testing::Test {
+ public:
+ URLRequestFtpJobTest()
+ : proxy_service_(new ProxyService(
+ new SimpleProxyConfigService, NULL, NULL)),
+ request_context_(&socket_factory_,
+ proxy_service_,
+ &network_delegate_) {
+ }
+
+ ~URLRequestFtpJobTest() {
+ // Clean up any remaining tasks that mess up unrelated tests.
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+
+ void AddSocket(MockRead* reads, size_t reads_size,
+ MockWrite* writes, size_t writes_size) {
+ DeterministicSocketData* socket_data = new DeterministicSocketData(
+ reads, reads_size, writes, writes_size);
+ socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ socket_data->StopAfter(reads_size + writes_size - 1);
+ socket_factory_.AddSocketDataProvider(socket_data);
+
+ socket_data_.push_back(socket_data);
+ }
+
+ URLRequestContext* request_context() { return &request_context_; }
+ TestNetworkDelegate* network_delegate() { return &network_delegate_; }
+ DeterministicSocketData* socket_data(size_t index) {
+ return socket_data_[index];
+ }
+
+ private:
+ DeterministicMockClientSocketFactory socket_factory_;
+ TestNetworkDelegate network_delegate_;
+
+ // Owned by |request_context_|:
+ ProxyService* proxy_service_;
+ std::vector<DeterministicSocketData*> socket_data_;
+
+ FtpTestURLRequestContext request_context_;
+};
+
+TEST_F(URLRequestFtpJobTest, FtpProxyRequest) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
+ "Host: ftp.example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 2, "Content-Length: 9\r\n\r\n"),
+ MockRead(ASYNC, 3, "test.html"),
+ };
+
+ AddSocket(reads, arraysize(reads), writes, arraysize(writes));
+
+ TestDelegate request_delegate;
+ URLRequest url_request(GURL("ftp://ftp.example.com/"),
+ &request_delegate,
+ request_context(),
+ network_delegate());
+ url_request.Start();
+ ASSERT_TRUE(url_request.is_pending());
+ socket_data(0)->RunFor(4);
+
+ EXPECT_TRUE(url_request.status().is_success());
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+ EXPECT_FALSE(request_delegate.auth_required_called());
+ EXPECT_EQ("test.html", request_delegate.data_received());
+}
+
+TEST_F(URLRequestFtpJobTest, FtpProxyRequestNeedAuth) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
+ "Host: ftp.example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ // No credentials.
+ MockRead(ASYNC, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
+ MockRead(ASYNC, 2, "Proxy-Authenticate: Basic "
+ "realm=\"MyRealm1\"\r\n"),
+ MockRead(ASYNC, 3, "Content-Length: 9\r\n\r\n"),
+ MockRead(ASYNC, 4, "test.html"),
+ };
+
+ AddSocket(reads, arraysize(reads), writes, arraysize(writes));
+
+ TestDelegate request_delegate;
+ URLRequest url_request(GURL("ftp://ftp.example.com/"),
+ &request_delegate,
+ request_context(),
+ network_delegate());
+ url_request.Start();
+ ASSERT_TRUE(url_request.is_pending());
+ socket_data(0)->RunFor(5);
+
+ EXPECT_TRUE(url_request.status().is_success());
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+ EXPECT_FALSE(request_delegate.auth_required_called());
+ EXPECT_EQ("test.html", request_delegate.data_received());
}
-TEST(FtpProtocolHandlerTest, CreateTransactionFails) {
- testing::InSequence in_sequence_;
+TEST_F(URLRequestFtpJobTest, FtpProxyRequestDoNotSaveCookies) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
+ "Host: ftp.example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 2, "Content-Length: 9\r\n"),
+ MockRead(ASYNC, 3, "Set-Cookie: name=value\r\n\r\n"),
+ MockRead(ASYNC, 4, "test.html"),
+ };
- ::testing::StrictMock<MockFtpTransactionFactory> ftp_transaction_factory;
- ::testing::StrictMock<MockURLRequestDelegate> delegate;
- FtpAuthCache ftp_auth_cache;
+ AddSocket(reads, arraysize(reads), writes, arraysize(writes));
- GURL url("ftp://example.com");
- URLRequestContext context;
- URLRequest url_request(url, &delegate, &context);
+ TestDelegate request_delegate;
+ URLRequest url_request(GURL("ftp://ftp.example.com/"),
+ &request_delegate,
+ request_context(),
+ network_delegate());
+ url_request.Start();
+ ASSERT_TRUE(url_request.is_pending());
- FtpProtocolHandler ftp_protocol_handler(
- &ftp_transaction_factory, &ftp_auth_cache);
+ socket_data(0)->RunFor(5);
- scoped_refptr<URLRequestJob> ftp_job(
- ftp_protocol_handler.MaybeCreateJob(&url_request, NULL));
- ASSERT_TRUE(ftp_job.get());
+ EXPECT_TRUE(url_request.status().is_success());
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+
+ // Make sure we do not accept cookies.
+ EXPECT_EQ(0, network_delegate()->set_cookie_count());
+
+ EXPECT_FALSE(request_delegate.auth_required_called());
+ EXPECT_EQ("test.html", request_delegate.data_received());
+}
+
+TEST_F(URLRequestFtpJobTest, FtpProxyRequestDoNotFollowRedirects) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
+ "Host: ftp.example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 302 Found\r\n"),
+ MockRead(ASYNC, 2, "Location: http://other.example.com/\r\n\r\n"),
+ };
+
+ AddSocket(reads, arraysize(reads), writes, arraysize(writes));
+
+ TestDelegate request_delegate;
+ URLRequest url_request(GURL("ftp://ftp.example.com/"),
+ &request_delegate,
+ request_context(),
+ network_delegate());
+ url_request.Start();
+ EXPECT_TRUE(url_request.is_pending());
- EXPECT_CALL(ftp_transaction_factory, CreateTransaction())
- .WillOnce(Return(static_cast<FtpTransaction*>(NULL)));
- ftp_job->Start();
- EXPECT_CALL(delegate, OnResponseStarted(_))
- .WillOnce(HandleOnResponseStarted(URLRequestStatus::FAILED));
MessageLoop::current()->RunUntilIdle();
- EXPECT_FALSE(url_request.is_pending());
+
+ EXPECT_TRUE(url_request.is_pending());
+ EXPECT_EQ(0, request_delegate.response_started_count());
+ EXPECT_EQ(0, network_delegate()->error_count());
+ ASSERT_TRUE(url_request.status().is_success());
+
+ socket_data(0)->RunFor(1);
+
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(1, network_delegate()->error_count());
+ EXPECT_FALSE(url_request.status().is_success());
+ EXPECT_EQ(ERR_UNSAFE_REDIRECT, url_request.status().error());
+}
+
+// We should re-use socket for requests using the same scheme, host, and port.
+TEST_F(URLRequestFtpJobTest, FtpProxyRequestReuseSocket) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/first HTTP/1.1\r\n"
+ "Host: ftp.example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ MockWrite(ASYNC, 4, "GET ftp://ftp.example.com/second HTTP/1.1\r\n"
+ "Host: ftp.example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 2, "Content-Length: 10\r\n\r\n"),
+ MockRead(ASYNC, 3, "test1.html"),
+ MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 6, "Content-Length: 10\r\n\r\n"),
+ MockRead(ASYNC, 7, "test2.html"),
+ };
+
+ AddSocket(reads, arraysize(reads), writes, arraysize(writes));
+
+ TestDelegate request_delegate1;
+ URLRequest url_request1(GURL("ftp://ftp.example.com/first"),
+ &request_delegate1,
+ request_context(),
+ network_delegate());
+ url_request1.Start();
+ ASSERT_TRUE(url_request1.is_pending());
+ socket_data(0)->RunFor(4);
+
+ EXPECT_TRUE(url_request1.status().is_success());
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+ EXPECT_FALSE(request_delegate1.auth_required_called());
+ EXPECT_EQ("test1.html", request_delegate1.data_received());
+
+ TestDelegate request_delegate2;
+ URLRequest url_request2(GURL("ftp://ftp.example.com/second"),
+ &request_delegate2,
+ request_context(),
+ network_delegate());
+ url_request2.Start();
+ ASSERT_TRUE(url_request2.is_pending());
+ socket_data(0)->RunFor(4);
+
+ EXPECT_TRUE(url_request2.status().is_success());
+ EXPECT_EQ(2, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+ EXPECT_FALSE(request_delegate2.auth_required_called());
+ EXPECT_EQ("test2.html", request_delegate2.data_received());
+}
+
+// We should not re-use socket when there are two requests to the same host,
+// but one is FTP and the other is HTTP.
+TEST_F(URLRequestFtpJobTest, FtpProxyRequestDoNotReuseSocket) {
+ MockWrite writes1[] = {
+ MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/first HTTP/1.1\r\n"
+ "Host: ftp.example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+ MockWrite writes2[] = {
+ MockWrite(ASYNC, 0, "GET /second HTTP/1.1\r\n"
+ "Host: ftp.example.com\r\n"
+ "Connection: keep-alive\r\n"
+ "User-Agent:\r\n"
+ "Accept-Encoding: gzip,deflate\r\n"
+ "Accept-Language: en-us,fr\r\n"
+ "Accept-Charset: iso-8859-1,*,utf-8\r\n\r\n"),
+ };
+ MockRead reads1[] = {
+ MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 2, "Content-Length: 10\r\n\r\n"),
+ MockRead(ASYNC, 3, "test1.html"),
+ };
+ MockRead reads2[] = {
+ MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 2, "Content-Length: 10\r\n\r\n"),
+ MockRead(ASYNC, 3, "test2.html"),
+ };
+
+ AddSocket(reads1, arraysize(reads1), writes1, arraysize(writes1));
+ AddSocket(reads2, arraysize(reads2), writes2, arraysize(writes2));
+
+ TestDelegate request_delegate1;
+ URLRequest url_request1(GURL("ftp://ftp.example.com/first"),
+ &request_delegate1,
+ request_context(),
+ network_delegate());
+ url_request1.Start();
+ ASSERT_TRUE(url_request1.is_pending());
+ socket_data(0)->RunFor(4);
+
+ EXPECT_TRUE(url_request1.status().is_success());
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+ EXPECT_FALSE(request_delegate1.auth_required_called());
+ EXPECT_EQ("test1.html", request_delegate1.data_received());
+
+ TestDelegate request_delegate2;
+ URLRequest url_request2(GURL("http://ftp.example.com/second"),
+ &request_delegate2,
+ request_context(),
+ network_delegate());
+ url_request2.Start();
+ ASSERT_TRUE(url_request2.is_pending());
+ socket_data(1)->RunFor(4);
+
+ EXPECT_TRUE(url_request2.status().is_success());
+ EXPECT_EQ(2, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+ EXPECT_FALSE(request_delegate2.auth_required_called());
+ EXPECT_EQ("test2.html", request_delegate2.data_received());
}
+} // namespace
+
} // namespace net
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index 19e1c0f..e523109 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -44,12 +44,14 @@ const int kStageDestruction = 1 << 10;
TestURLRequestContext::TestURLRequestContext()
: initialized_(false),
+ client_socket_factory_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(context_storage_(this)) {
Init();
}
TestURLRequestContext::TestURLRequestContext(bool delay_initialization)
: initialized_(false),
+ client_socket_factory_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(context_storage_(this)) {
if (!delay_initialization)
Init();
@@ -92,17 +94,20 @@ void TestURLRequestContext::Init() {
context_storage_.set_transport_security_state(
new TransportSecurityState());
}
- HttpNetworkSession::Params params;
- params.host_resolver = host_resolver();
- params.cert_verifier = cert_verifier();
- params.proxy_service = proxy_service();
- params.ssl_config_service = ssl_config_service();
- params.http_auth_handler_factory = http_auth_handler_factory();
- params.network_delegate = network_delegate();
- params.http_server_properties = http_server_properties();
- params.net_log = net_log();
-
- if (!http_transaction_factory()) {
+ if (http_transaction_factory()) {
+ // Make sure we haven't been passed an object we're not going to use.
+ EXPECT_FALSE(client_socket_factory_);
+ } else {
+ HttpNetworkSession::Params params;
+ params.client_socket_factory = client_socket_factory();
+ params.host_resolver = host_resolver();
+ params.cert_verifier = cert_verifier();
+ params.proxy_service = proxy_service();
+ params.ssl_config_service = ssl_config_service();
+ params.http_auth_handler_factory = http_auth_handler_factory();
+ params.network_delegate = network_delegate();
+ params.http_server_properties = http_server_properties();
+ params.net_log = net_log();
context_storage_.set_http_transaction_factory(new HttpCache(
new HttpNetworkSession(params),
HttpCache::DefaultBackend::InMemory(0)));
diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h
index 361f51d..fcaa30e 100644
--- a/net/url_request/url_request_test_util.h
+++ b/net/url_request/url_request_test_util.h
@@ -58,9 +58,19 @@ class TestURLRequestContext : public URLRequestContext {
void Init();
+ ClientSocketFactory* client_socket_factory() {
+ return client_socket_factory_;
+ }
+ void set_client_socket_factory(ClientSocketFactory* factory) {
+ client_socket_factory_ = factory;
+ }
+
private:
bool initialized_;
+ // Not owned:
+ ClientSocketFactory* client_socket_factory_;
+
protected:
URLRequestContextStorage context_storage_;
};