diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_network_transaction_spdy2_unittest.cc | 7 | ||||
-rw-r--r-- | net/http/http_network_transaction_spdy3_unittest.cc | 7 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl.cc | 9 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl_job.cc | 13 | ||||
-rw-r--r-- | net/http/http_util_icu.cc | 5 | ||||
-rw-r--r-- | net/socket/client_socket_pool_manager.cc | 6 | ||||
-rw-r--r-- | net/url_request/url_request_ftp_job.cc | 207 | ||||
-rw-r--r-- | net/url_request/url_request_ftp_job.h | 24 | ||||
-rw-r--r-- | net/url_request/url_request_ftp_job_unittest.cc | 378 | ||||
-rw-r--r-- | net/url_request/url_request_test_util.cc | 27 | ||||
-rw-r--r-- | net/url_request/url_request_test_util.h | 10 |
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_; }; |