diff options
author | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-05 08:56:21 +0000 |
---|---|---|
committer | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-05 08:56:21 +0000 |
commit | 29c494224c84b5393dbd9de92994dbb2b763ca15 (patch) | |
tree | 1989d9a10a1cc4a5cc4334cc8c7e1d5054345022 /net/url_request | |
parent | aca4eca3f4fc0da7a4bfe8c57815b5a746c4782f (diff) | |
download | chromium_src-29c494224c84b5393dbd9de92994dbb2b763ca15.zip chromium_src-29c494224c84b5393dbd9de92994dbb2b763ca15.tar.gz chromium_src-29c494224c84b5393dbd9de92994dbb2b763ca15.tar.bz2 |
Remove WinInet FTP code.
TEST=none
BUG=25520
Review URL: http://codereview.chromium.org/523034
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@35524 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/url_request')
-rw-r--r-- | net/url_request/url_request_ftp_job.cc | 562 | ||||
-rw-r--r-- | net/url_request/url_request_ftp_job.h | 116 | ||||
-rw-r--r-- | net/url_request/url_request_inet_job.cc | 371 | ||||
-rw-r--r-- | net/url_request/url_request_inet_job.h | 159 | ||||
-rw-r--r-- | net/url_request/url_request_job_manager.cc | 8 |
5 files changed, 0 insertions, 1216 deletions
diff --git a/net/url_request/url_request_ftp_job.cc b/net/url_request/url_request_ftp_job.cc index bc7f085..e69de29 100644 --- a/net/url_request/url_request_ftp_job.cc +++ b/net/url_request/url_request_ftp_job.cc @@ -1,562 +0,0 @@ -// Copyright (c) 2006-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/url_request/url_request_ftp_job.h" - -#include <windows.h> -#include <wininet.h> - -#include "base/message_loop.h" -#include "base/string_util.h" -#include "base/sys_string_conversions.h" -#include "base/time.h" -#include "net/base/auth.h" -#include "net/base/escape.h" -#include "net/base/load_flags.h" -#include "net/base/net_errors.h" -#include "net/base/net_util.h" -#include "net/base/wininet_util.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" -#include "net/url_request/url_request_new_ftp_job.h" - -using std::string; - -using net::WinInetUtil; - -// When building the directory listing, the period to wait before notifying -// the parent class that we wrote the data. -#define kFtpBufferTimeMs 50 - -static bool UnescapeAndValidatePath(const URLRequest* request, - std::string* unescaped_path) { - // Path in GURL is %-encoded UTF-8. FTP servers do not - // understand %-escaped path so that we have to unescape leading to an - // unescaped UTF-8 path. Then, the presence of NULL, CR and LF is checked - // because they're not allowed in FTP. - // TODO(jungshik) Even though RFC 2640 specifies that UTF-8 be used. - // There are many FTP servers that use legacy encodings. For them, - // we need to identify the encoding and convert to that encoding. - static const std::string kInvalidChars("\x00\x0d\x0a", 3); - *unescaped_path = UnescapeURLComponent(request->url().path(), - UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS); - if (unescaped_path->find_first_of(kInvalidChars) != std::string::npos) { - SetLastError(ERROR_INTERNET_INVALID_URL); - // GURL path should not contain '%00' which is NULL(0x00) when unescaped. - // URLRequestFtpJob should not have been invoked for an invalid GURL. - DCHECK(unescaped_path->find(std::string("\x00", 1)) == std::string::npos) << - "Path should not contain %00."; - return false; - } - return true; -} - -// static -URLRequestJob* URLRequestFtpJob::Factory(URLRequest* request, - const std::string &scheme) { - // Checking whether we are using new or old FTP implementation. - if (request->context() && request->context()->ftp_transaction_factory()) - return URLRequestNewFtpJob::Factory(request, scheme); - - DCHECK(scheme == "ftp"); - - int port = request->url().IntPort(); - - if (request->url().has_port() && - !net::IsPortAllowedByFtp(port) && !net::IsPortAllowedByOverride(port)) - return new URLRequestErrorJob(request, net::ERR_UNSAFE_PORT); - - return new URLRequestFtpJob(request); -} - -URLRequestFtpJob::URLRequestFtpJob(URLRequest* request) - : URLRequestInetJob(request), state_(START), is_directory_(false), - dest_(NULL), dest_size_(0) { -} - -URLRequestFtpJob::~URLRequestFtpJob() { -} - -void URLRequestFtpJob::Start() { - GURL parts(request_->url()); - const std::string& scheme = parts.scheme(); - - // We should only be dealing with FTP at this point: - DCHECK(LowerCaseEqualsASCII(scheme, "ftp")); - - SendRequest(); -} - -bool URLRequestFtpJob::GetMimeType(std::string* mime_type) const { - if (!is_directory_) - return false; - - mime_type->assign("text/html"); - return true; -} - -void URLRequestFtpJob::OnCancelAuth() { - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - this, &URLRequestFtpJob::ContinueNotifyHeadersComplete)); -} - -void URLRequestFtpJob::OnSetAuth() { - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - this, &URLRequestFtpJob::SendRequest)); -} - -void URLRequestFtpJob::SendRequest() { - state_ = CONNECTING; - - DWORD flags = - INTERNET_FLAG_KEEP_CONNECTION | - INTERNET_FLAG_EXISTING_CONNECT | - INTERNET_FLAG_PASSIVE | - INTERNET_FLAG_RAW_DATA; - - // It doesn't make sense to ask for both a cache validation and a - // reload at the same time. - DCHECK(!((request_->load_flags() & net::LOAD_VALIDATE_CACHE) && - (request_->load_flags() & net::LOAD_BYPASS_CACHE))); - - if (request_->load_flags() & net::LOAD_BYPASS_CACHE) - flags |= INTERNET_FLAG_RELOAD; - - // Apply authentication if we have any, otherwise authenticate - // according to FTP defaults. (See InternetConnect documentation.) - // First, check if we have auth in cache, then check URL. - // That way a user can re-enter credentials, and we'll try with their - // latest input rather than always trying what they specified - // in the url (if anything). - string username, password; - bool have_auth = false; - if (server_auth_ && server_auth_->state == net::AUTH_STATE_HAVE_AUTH) { - // Add auth info to cache - have_auth = true; - username = WideToUTF8(server_auth_->username); - password = WideToUTF8(server_auth_->password); - request_->context()->ftp_auth_cache()->Add(request_->url().GetOrigin(), - server_auth_->username, - server_auth_->password); - } else { - if (request_->url().has_username()) { - username = request_->url().username(); - password = request_->url().has_password() ? request_->url().password() : - ""; - have_auth = true; - } - } - - int port = request_->url().has_port() ? - request_->url().IntPort() : INTERNET_DEFAULT_FTP_PORT; - - connection_handle_ = InternetConnectA( - GetTheInternet(), - request_->url().HostNoBrackets().c_str(), - port, - have_auth ? username.c_str() : NULL, - have_auth ? password.c_str() : NULL, - INTERNET_SERVICE_FTP, flags, - reinterpret_cast<DWORD_PTR>(this)); - - if (connection_handle_) { - OnConnect(); - } else { - ProcessRequestError(GetLastError()); - } -} - -void URLRequestFtpJob::OnIOComplete(const AsyncResult& result) { - if (state_ == CONNECTING) { - switch (result.dwError) { - case ERROR_NO_MORE_FILES: - // url is an empty directory - OnStartDirectoryTraversal(); - OnFinishDirectoryTraversal(); - return; - case ERROR_INTERNET_LOGIN_FAILURE: - // fall through - case ERROR_INTERNET_INCORRECT_USER_NAME: - // fall through - case ERROR_INTERNET_INCORRECT_PASSWORD: { - GURL origin = request_->url().GetOrigin(); - if (server_auth_ != NULL && - server_auth_->state == net::AUTH_STATE_HAVE_AUTH) { - request_->context()->ftp_auth_cache()->Remove(origin, - server_auth_->username, - server_auth_->password); - } else { - server_auth_ = new net::AuthData(); - } - server_auth_->state = net::AUTH_STATE_NEED_AUTH; - - net::FtpAuthCache::Entry* cached_auth = - request_->context()->ftp_auth_cache()->Lookup(origin); - - if (cached_auth) { - // Retry using cached auth data. - SetAuth(cached_auth->username, cached_auth->password); - } else { - // The io completed fine, the error was due to invalid auth. - SetStatus(URLRequestStatus()); - - // Prompt for a username/password. - NotifyHeadersComplete(); - } - return; - } - case ERROR_SUCCESS: - connection_handle_ = (HINTERNET)result.dwResult; - OnConnect(); - return; - case ERROR_INTERNET_EXTENDED_ERROR: { - DWORD extended_err(ERROR_SUCCESS); - DWORD size = 1; - char buffer[1]; - if (!InternetGetLastResponseInfoA(&extended_err, buffer, &size)) - // We don't care about the error text here, so the only acceptable - // error is one regarding insufficient buffer length. - DCHECK(GetLastError() == ERROR_INSUFFICIENT_BUFFER); - if (extended_err != ERROR_SUCCESS) { - CleanupConnection(); - NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, - WinInetUtil::OSErrorToNetError(extended_err))); - return; - } - // Fall through in the case we saw ERROR_INTERNET_EXTENDED_ERROR but - // InternetGetLastResponseInfo gave us no additional information. - } - default: - CleanupConnection(); - NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, - WinInetUtil::OSErrorToNetError(result.dwError))); - return; - } - } else if (state_ == SETTING_CUR_DIRECTORY) { - OnSetCurrentDirectory(result.dwError); - } else if (state_ == FINDING_FIRST_FILE) { - // We don't fail here if result.dwError != ERROR_SUCCESS because - // getting an error here doesn't always mean the file is not found. - // FindFirstFileA() issue a LIST command and may fail on some - // ftp server when the requested object is a file. So ERROR_NO_MORE_FILES - // from FindFirstFileA() is not a reliable criteria for valid path - // or not, we should proceed optimistically by getting the file handle. - if (result.dwError != ERROR_SUCCESS && - result.dwError != ERROR_NO_MORE_FILES) { - DWORD result_error = result.dwError; - CleanupConnection(); - NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, - WinInetUtil::OSErrorToNetError(result_error))); - return; - } - request_handle_ = (HINTERNET)result.dwResult; - OnFindFirstFile(result.dwError); - } else if (state_ == GETTING_DIRECTORY) { - OnFindFile(result.dwError); - } else if (state_ == GETTING_FILE_HANDLE) { - if (result.dwError != ERROR_SUCCESS) { - CleanupConnection(); - NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, - WinInetUtil::OSErrorToNetError(result.dwError))); - return; - } - // start reading file contents - state_ = GETTING_FILE; - request_handle_ = (HINTERNET)result.dwResult; - NotifyHeadersComplete(); - } else { - // we don't have IO outstanding. Pass to our base class. - URLRequestInetJob::OnIOComplete(result); - } -} - -bool URLRequestFtpJob::NeedsAuth() { - // 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 - // Proxy-Authenticate protocol when proxy servers require auth. - return server_auth_ && server_auth_->state == net::AUTH_STATE_NEED_AUTH; -} - -void URLRequestFtpJob::GetAuthChallengeInfo( - scoped_refptr<net::AuthChallengeInfo>* result) { - DCHECK((server_auth_ != NULL) && - (server_auth_->state == net::AUTH_STATE_NEED_AUTH)); - scoped_refptr<net::AuthChallengeInfo> auth_info = new net::AuthChallengeInfo; - auth_info->is_proxy = false; - auth_info->host_and_port = ASCIIToWide( - net::GetHostAndPort(request_->url())); - auth_info->scheme = L""; - auth_info->realm = L""; - result->swap(auth_info); -} - -void URLRequestFtpJob::OnConnect() { - DCHECK_EQ(state_, CONNECTING); - - state_ = SETTING_CUR_DIRECTORY; - // Setting the directory lets us determine if the URL is a file, - // and also keeps the working directory for the FTP session in sync - // with what is being displayed in the browser. - if (request_->url().has_path()) { - std::string unescaped_path; - if (UnescapeAndValidatePath(request_, &unescaped_path) && - FtpSetCurrentDirectoryA(connection_handle_, - unescaped_path.c_str())) { - OnSetCurrentDirectory(ERROR_SUCCESS); - } else { - ProcessRequestError(GetLastError()); - } - } -} - -void URLRequestFtpJob::OnSetCurrentDirectory(DWORD last_error) { - DCHECK_EQ(state_, SETTING_CUR_DIRECTORY); - - is_directory_ = (last_error == ERROR_SUCCESS); - // if last_error is not ERROR_SUCCESS, the requested url is either - // a file or an invalid path. We optimistically try to read as a file, - // and if it fails, we fail. - state_ = FINDING_FIRST_FILE; - - std::string unescaped_path; - bool is_path_valid = true; - if (request_->url().has_path()) { - is_path_valid = UnescapeAndValidatePath(request_, &unescaped_path); - } - if (is_path_valid && - (request_handle_ = FtpFindFirstFileA(connection_handle_, - unescaped_path.c_str(), - &find_data_, 0, - reinterpret_cast<DWORD_PTR>(this)))) { - OnFindFirstFile(GetLastError()); - } else { - ProcessRequestError(GetLastError()); - } -} - -void URLRequestFtpJob::FindNextFile() { - DWORD last_error; - if (InternetFindNextFileA(request_handle_, &find_data_)) { - last_error = ERROR_SUCCESS; - } else { - last_error = GetLastError(); - // We'll get ERROR_NO_MORE_FILES if the directory is empty. - if (last_error != ERROR_NO_MORE_FILES) { - ProcessRequestError(last_error); - return; - } - } - // Use InvokeLater to call OnFindFile as it ends up calling us, so we don't - // to blow the stack. - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - this, &URLRequestFtpJob::OnFindFile, last_error)); -} - -void URLRequestFtpJob::OnFindFirstFile(DWORD last_error) { - DCHECK_EQ(state_, FINDING_FIRST_FILE); - if (!is_directory_) { - // Note that it is not enough to just check !is_directory_ and assume - // the URL is a file, because is_directory_ is true iff we successfully - // set current directory to the URL path. Therefore, the URL could just - // be an invalid path. We proceed optimistically and fail in that case. - state_ = GETTING_FILE_HANDLE; - std::string unescaped_path; - if (UnescapeAndValidatePath(request_, &unescaped_path) && - (request_handle_ = FtpOpenFileA(connection_handle_, - unescaped_path.c_str(), - GENERIC_READ, - INTERNET_FLAG_TRANSFER_BINARY, - reinterpret_cast<DWORD_PTR>(this)))) { - // Start reading file contents - state_ = GETTING_FILE; - NotifyHeadersComplete(); - } else { - ProcessRequestError(GetLastError()); - } - } else { - OnStartDirectoryTraversal(); - // If we redirect in OnStartDirectoryTraversal() then this request job - // is cancelled. - if (request_handle_) - OnFindFile(last_error); - } -} - -void URLRequestFtpJob::OnFindFile(DWORD last_error) { - DCHECK_EQ(state_, GETTING_DIRECTORY); - - if (last_error == ERROR_SUCCESS) { - // TODO(jabdelmalek): need to add icons for files/folders. - int64 size = - (static_cast<unsigned __int64>(find_data_.nFileSizeHigh) << 32) | - find_data_.nFileSizeLow; - - // We don't know the encoding used on an FTP server, but we - // use FtpFindFirstFileA, which I guess does NOT preserve - // the raw byte sequence because it's implemented in terms - // of FtpFindFirstFileW. Without the raw byte sequence, we - // can't apply the encoding detection or other heuristics - // to determine/guess the encoding. Neither can we use UTF-8 - // used by a RFC-2640-compliant FTP server. In some cases (e.g. - // the default code page is an SBCS with almost all bytes assigned. - // In lucky cases, it's even possible with a DBCS), it's possible - // to recover the raw byte sequence in most cases. We can do - // some more here, but it's not worth the effort because we're - // going to replace this class with URLRequestNewFtpJob. - string file_entry = net::GetDirectoryListingEntry( - base::SysNativeMBToWide(find_data_.cFileName), std::string(), - false, size, base::Time::FromFileTime(find_data_.ftLastWriteTime)); - WriteData(&file_entry, true); - - FindNextFile(); - return; - } - - DCHECK(last_error == ERROR_NO_MORE_FILES); - OnFinishDirectoryTraversal(); -} - -void URLRequestFtpJob::OnStartDirectoryTraversal() { - state_ = GETTING_DIRECTORY; - - // Unescape the URL path and pass the raw 8bit directly to the browser. - // - // Here we can try to detect the encoding although it may not be very - // reliable because it's not likely to be long enough. Because this class - // will be replaced by URLRequestNewFtpJob and is used only on Windows, - // we use SysNativeMBToWide as a stopgap measure. - string html = net::GetDirectoryListingHeader( - base::SysNativeMBToWide(UnescapeURLComponent(request_->url().path(), - UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS))); - - // If this isn't top level directory (i.e. the path isn't "/",) add a link to - // the parent directory. - if (request_->url().path().length() > 1) - html.append(net::GetDirectoryListingEntry(L"..", std::string(), - false, 0, base::Time())); - - WriteData(&html, true); - - NotifyHeadersComplete(); -} - -void URLRequestFtpJob::OnFinishDirectoryTraversal() { - state_ = DONE; -} - -int URLRequestFtpJob::WriteData(const std::string* data, - bool call_io_complete) { - int written = 0; - - if (data && data->length()) - directory_html_.append(*data); - - if (dest_) { - size_t bytes_to_copy = std::min(static_cast<size_t>(dest_size_), - directory_html_.length()); - if (bytes_to_copy) { - memcpy(dest_, directory_html_.c_str(), bytes_to_copy); - directory_html_.erase(0, bytes_to_copy); - dest_ = NULL; - dest_size_ = NULL; - written = static_cast<int>(bytes_to_copy); - - if (call_io_complete) { - // Wait a little bit before telling the parent class that we wrote - // data. This avoids excessive cycles of us getting one file entry and - // telling the parent class to Read(). - MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod( - this, &URLRequestFtpJob::ContinueIOComplete, written), - kFtpBufferTimeMs); - } - } - } - - return written; -} - -void URLRequestFtpJob::ContinueIOComplete(int bytes_written) { - AsyncResult result; - result.dwResult = bytes_written; - result.dwError = ERROR_SUCCESS; - URLRequestInetJob::OnIOComplete(result); -} - -void URLRequestFtpJob::ContinueNotifyHeadersComplete() { - NotifyHeadersComplete(); -} - -int URLRequestFtpJob::CallInternetRead(char* dest, int dest_size, - int *bytes_read) { - int result; - - if (is_directory_) { - // Copy the html that we created from the directory listing that we got - // from InternetFindNextFile. - DCHECK(dest_ == NULL); - dest_ = dest; - dest_size_ = dest_size; - - DCHECK(state_ == GETTING_DIRECTORY || state_ == DONE); - int written = WriteData(NULL, false); - if (written) { - *bytes_read = written; - result = ERROR_SUCCESS; - } else { - result = state_ == GETTING_DIRECTORY ? ERROR_IO_PENDING : ERROR_SUCCESS; - } - } else { - DWORD bytes_to_read = dest_size; - bytes_read_ = 0; - // InternetReadFileEx doesn't work for asynchronous FTP, InternetReadFile - // must be used instead. - if (!InternetReadFile(request_handle_, dest, bytes_to_read, &bytes_read_)) - return GetLastError(); - - *bytes_read = static_cast<int>(bytes_read_); - result = ERROR_SUCCESS; - } - - return result; -} - -bool URLRequestFtpJob::GetReadBytes(const AsyncResult& result, - int* bytes_read) { - if (is_directory_) { - *bytes_read = static_cast<int>(result.dwResult); - } else { - if (!result.dwResult) - return false; - - // IE5 and later return the number of read bytes in the - // INTERNET_ASYNC_RESULT structure. IE4 holds on to the pointer passed in - // to InternetReadFile and store it there. - *bytes_read = bytes_read_; - - if (!*bytes_read) - *bytes_read = result.dwError; - } - - return true; -} - -bool URLRequestFtpJob::IsRedirectResponse(GURL* location, - int* http_status_code) { - if (is_directory_) { - std::string ftp_path = request_->url().path(); - if (!ftp_path.empty() && ('/' != ftp_path[ftp_path.length() - 1])) { - ftp_path.push_back('/'); - GURL::Replacements replacements; - replacements.SetPathStr(ftp_path); - - *location = request_->url().ReplaceComponents(replacements); - *http_status_code = 301; // simulate a permanent redirect - return true; - } - } - - return false; -} diff --git a/net/url_request/url_request_ftp_job.h b/net/url_request/url_request_ftp_job.h index a2b2aea..e69de29 100644 --- a/net/url_request/url_request_ftp_job.h +++ b/net/url_request/url_request_ftp_job.h @@ -1,116 +0,0 @@ -// Copyright (c) 2006-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. - -#ifndef NET_URL_REQUEST_URL_REQUEST_FTP_JOB_H_ -#define NET_URL_REQUEST_URL_REQUEST_FTP_JOB_H_ - -#include <string> - -#include "net/url_request/url_request_inet_job.h" - -// A basic FTP job that handles download files and showing directory listings. -class URLRequestFtpJob : public URLRequestInetJob { - public: - static URLRequestJob* Factory(URLRequest* request, const std::string& scheme); - - // URLRequestJob methods: - virtual void Start(); - virtual bool GetMimeType(std::string* mime_type) const; - - // URLRequestInetJob methods: - virtual void OnIOComplete(const AsyncResult& result); - - protected: - explicit URLRequestFtpJob(URLRequest* request); - - // Starts the WinInet request. - virtual void SendRequest(); - - virtual int CallInternetRead(char* dest, int dest_size, int *bytes_read); - virtual bool GetReadBytes(const AsyncResult& result, int* bytes_read); - virtual void OnCancelAuth(); - virtual void OnSetAuth(); - virtual bool NeedsAuth(); - virtual void GetAuthChallengeInfo(scoped_refptr<net::AuthChallengeInfo>*); - virtual bool IsRedirectResponse(GURL* location, int* http_status_code); - - private: - virtual ~URLRequestFtpJob(); - - // Called after InternetConnect successfully connects to server. - void OnConnect(); - - // Called after FtpSetCurrentDirectory attempts to change current dir. - void OnSetCurrentDirectory(DWORD last_error); - - // Requests the next file in the directory listing from WinInet. - void FindNextFile(); - - // Called when the first file in a directory listing is available. - void OnFindFirstFile(DWORD last_error); - - // Called when a file in a directory listing is available. - void OnFindFile(DWORD last_error); - - // Call this when starting a directory listing to setup the html. - void OnStartDirectoryTraversal(); - - // Call this at the end of a directory listing to complete the html. - void OnFinishDirectoryTraversal(); - - // If given data, writes it to the directory listing html. If - // call_io_complete is true, will also notify the parent class that we wrote - // data in the given buffer. - int WriteData(const std::string* data, bool call_io_complete); - - // Continuation function for calling OnIOComplete through the message loop. - virtual void ContinueIOComplete(int bytes_written); - - // Continuation function for calling NotifyHeadersComplete through the message - // loop. - virtual void ContinueNotifyHeadersComplete(); - - typedef enum { - // Initial state of the ftp job. - START = 0x200, - // Opening the url. - CONNECTING, - // Attempting to change current dir to match request. - SETTING_CUR_DIRECTORY, - // Retrieving first file information in cur dir (by FtpFindFirstFile). - FINDING_FIRST_FILE, - // Retrieving the directory listing (if directory). - GETTING_DIRECTORY, - // Initiate access to file by call to FtpOpenFile (if file). - GETTING_FILE_HANDLE, - // Retrieving the file (if file). - GETTING_FILE, - // URLRequestInetJob is reading the response now. - DONE - } FtpJobState; - - // The FtpJob has several asynchronous operations which happen - // in sequence. The state keeps track of which asynchronous IO - // is pending at any given point in time. - FtpJobState state_; - - // In IE 4 and before, this pointer passed to asynchronous InternetReadFile - // calls is where the number of read bytes is written to. - DWORD bytes_read_; - - bool is_directory_; // does the url point to a file or directory - WIN32_FIND_DATAA find_data_; - std::string directory_html_; // if url is directory holds html - - // When building a directory listing, we need to temporarily hold on to the - // buffer in between the time a Read() call comes in and we get the file - // entry from WinInet. - char* dest_; - int dest_size_; - - - DISALLOW_EVIL_CONSTRUCTORS(URLRequestFtpJob); -}; - -#endif // NET_URL_REQUEST_URL_REQUEST_FTP_JOB_H_ diff --git a/net/url_request/url_request_inet_job.cc b/net/url_request/url_request_inet_job.cc index 9791fcd8..e69de29 100644 --- a/net/url_request/url_request_inet_job.cc +++ b/net/url_request/url_request_inet_job.cc @@ -1,371 +0,0 @@ -// Copyright (c) 2006-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/url_request/url_request_inet_job.h" - -#include <algorithm> - -#include "base/message_loop.h" -#include "base/string_util.h" -#include "googleurl/src/gurl.h" -#include "net/base/auth.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" -#include "net/base/net_util.h" -#include "net/base/wininet_util.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_error_job.h" -#include "net/url_request/url_request_ftp_job.h" -#include "net/url_request/url_request_job_metrics.h" -#include "net/url_request/url_request_job_tracker.h" - -using net::WinInetUtil; - -// -// HOW ASYNC IO WORKS -// -// The URLRequestInet* classes are now fully asynchronous. This means that -// all IO operations pass buffers into WinInet, and as WinInet completes those -// IO requests, it will fill the buffer, and then callback to the client. -// Asynchronous IO Operations include: -// HttpSendRequestEx -// InternetWriteFile -// HttpEndRequest -// InternetOpenUrl -// InternetReadFile (for FTP) -// InternetReadFileEx (for HTTP) -// InternetCloseHandle -// -// To understand how this works, you need to understand the basic class -// hierarchy for the URLRequestJob classes: -// -// URLRequestJob -// | -// +--------------+-------------------+ -// | | -// (Other Job Types) URLRequestInetJob -// e.g. | | -// URLRequestFileJob URLRequestFtpJob URLRequestHttpJob -// | -// URLRequestHttpUploadJob -// -// -// To make this work, each URLRequestInetJob has a virtual method called -// OnIOComplete(). If a derived URLRequestInetJob class issues -// an asynchronous IO, it must override the OnIOComplete method -// to handle the IO completion. Once it has overridden this method, -// *all* asynchronous IO completions will come to this method, even -// those asynchronous IOs which may have been issued by a base class. -// For example, URLRequestInetJob has methods which Read from the -// connection asynchronously. Once URLRequestHttpJob overrides -// OnIOComplete (so that it can receive its own async IO callbacks) -// it will also receive the URLRequestInetJob async IO callbacks. To -// make this work, the derived class must track its own state, and call -// the base class' version of OnIOComplete if appropriate. -// - -COMPILE_ASSERT( - sizeof(URLRequestInetJob::AsyncResult) == sizeof(INTERNET_ASYNC_RESULT), - async_result_inconsistent_size); - -HINTERNET URLRequestInetJob::the_internet_ = NULL; -#ifndef NDEBUG -MessageLoop* URLRequestInetJob::my_message_loop_ = NULL; -#endif - -URLRequestInetJob::URLRequestInetJob(URLRequest* request) - : URLRequestJob(request), - connection_handle_(NULL), - request_handle_(NULL), - last_error_(ERROR_SUCCESS), - is_waiting_(false), - read_in_progress_(false), - loop_(MessageLoop::current()) { - // TODO(darin): we should re-create the internet if the UA string changes, - // but we have to be careful about existing users of this internet. - if (!the_internet_) { - InitializeTheInternet(request->context() ? - request->context()->GetUserAgent(GURL()) : std::string()); - } -#ifndef NDEBUG - DCHECK(MessageLoop::current() == my_message_loop_) << - "All URLRequests should happen on the same thread"; -#endif -} - -URLRequestInetJob::~URLRequestInetJob() { - DCHECK(!request_) << "request should be detached at this point"; - - // The connections may have already been cleaned up. It is ok to call - // CleanupConnection again to make sure the resource is properly released. - // See bug 684997. - CleanupConnection(); -} - -void URLRequestInetJob::Kill() { - CleanupConnection(); - - { - AutoLock locked(loop_lock_); - loop_ = NULL; - } - - // Dispatch the NotifyDone message to the URLRequest - URLRequestJob::Kill(); -} - -void URLRequestInetJob::SetAuth(const std::wstring& username, - const std::wstring& password) { - DCHECK((proxy_auth_ && proxy_auth_->state == net::AUTH_STATE_NEED_AUTH) || - (server_auth_ && server_auth_->state == net::AUTH_STATE_NEED_AUTH)); - - // Proxy gets set first, then WWW. - net::AuthData* auth = - (proxy_auth_ && proxy_auth_->state == net::AUTH_STATE_NEED_AUTH ? - proxy_auth_.get() : server_auth_.get()); - - if (auth) { - auth->state = net::AUTH_STATE_HAVE_AUTH; - auth->username = username; - auth->password = password; - } - - // Resend the request with the new username and password. - // Do this asynchronously in case we were called from within a - // NotifyDataAvailable callback. - // TODO(mpcomplete): hmm... is it possible 'this' gets deleted before the task - // is run? - OnSetAuth(); -} - -void URLRequestInetJob::CancelAuth() { - DCHECK((proxy_auth_ && proxy_auth_->state == net::AUTH_STATE_NEED_AUTH) || - (server_auth_ && server_auth_->state == net::AUTH_STATE_NEED_AUTH)); - - // Proxy gets set first, then WWW. - net::AuthData* auth = - (proxy_auth_ && proxy_auth_->state == net::AUTH_STATE_NEED_AUTH ? - proxy_auth_.get() : server_auth_.get()); - - if (auth) { - auth->state = net::AUTH_STATE_CANCELED; - } - - // Once the auth is cancelled, we proceed with the request as though - // there were no auth. So, send the OnResponseStarted. Schedule this - // for later so that we don't cause any recursing into the caller - // as a result of this call. - OnCancelAuth(); -} - -void URLRequestInetJob::OnIOComplete(const AsyncResult& result) { - URLRequestStatus status; - - if (read_in_progress_) { - read_in_progress_ = false; - int bytes_read = 0; - if (GetReadBytes(result, &bytes_read)) { - SetStatus(status); - if (bytes_read == 0) { - NotifyDone(status); - CleanupConnection(); - } - } else { - bytes_read = -1; - URLRequestStatus status; - status.set_status(URLRequestStatus::FAILED); - status.set_os_error(WinInetUtil::OSErrorToNetError(result.dwError)); - NotifyDone(status); - CleanupConnection(); - } - NotifyReadComplete(bytes_read); - } else { - // If we get here, an IO is completing which we didn't - // start or we lost track of our state. - NOTREACHED(); - } -} - -bool URLRequestInetJob::ReadRawData(net::IOBuffer* dest, int dest_size, - int *bytes_read) { - if (is_done()) - return 0; - - DCHECK_NE(dest_size, 0); - DCHECK_NE(bytes_read, (int*)NULL); - DCHECK(!read_in_progress_); - - *bytes_read = 0; - - int result = CallInternetRead(dest->data(), dest_size, bytes_read); - if (result == ERROR_SUCCESS) { - DLOG(INFO) << "read " << *bytes_read << " bytes"; - if (*bytes_read == 0) - CleanupConnection(); // finished reading all the data - return true; - } - - if (ProcessRequestError(result)) - read_in_progress_ = true; - - // Whether we had an error or the request is pending. - // Both of these cases return false. - return false; -} - -void URLRequestInetJob::CallOnIOComplete(const AsyncResult& result) { - // It's important to clear this flag before calling OnIOComplete - is_waiting_ = false; - - // the job could have completed with an error while the message was pending - if (!is_done()) { - // Verify that our status is currently set to IO_PENDING and - // reset it on success. - DCHECK(GetStatus().is_io_pending()); - if (result.dwResult && result.dwError == 0) - SetStatus(URLRequestStatus()); - - OnIOComplete(result); - } - - Release(); // may destroy self if last reference -} - -bool URLRequestInetJob::ProcessRequestError(int error) { - if (error == ERROR_IO_PENDING) { - DLOG(INFO) << "waiting for WinInet call to complete"; - AddRef(); // balanced in CallOnIOComplete - is_waiting_ = true; - SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); - return true; - } - DLOG(ERROR) << "WinInet call failed: " << error; - CleanupConnection(); - NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, - WinInetUtil::OSErrorToNetError(error))); - return false; -} - -void URLRequestInetJob::CleanupConnection() { - if (!request_handle_ && !connection_handle_) - return; // nothing to clean up - - if (request_handle_) { - CleanupHandle(request_handle_); - request_handle_ = NULL; - } - if (connection_handle_) { - CleanupHandle(connection_handle_); - connection_handle_ = NULL; - } -} - -void URLRequestInetJob::CleanupHandle(HINTERNET handle) { - // We no longer need notifications from this connection. - InternetSetStatusCallback(handle, NULL); - - if (!InternetCloseHandle(handle)) { - // InternetCloseHandle is evil. The documentation specifies that it - // either succeeds immediately or returns ERROR_IO_PENDING if there is - // something outstanding, in which case the close will happen automagically - // later. In either of these cases, it will call us back with - // INTERNET_STATUS_HANDLE_CLOSING (because we set up the async callbacks) - // and we simply do nothing for the message. - // - // However, sometimes it also seems to fail with ERROR_INVALID_HANDLE. - // This seems to happen when we cancel before it has called us back with - // data. For example, if we cancel during DNS resolution or while waiting - // for a slow server. - // - // Our speculation is that in these cases WinInet creates a handle for - // us with an internal structure, but that the driver has not yet called - // it back with a "real" handle (the driver level is probably what - // generates IO_PENDING). The driver has not yet specified a handle, which - // causes WinInet to barf. - // - // However, in this case, the cancel seems to work. The TCP connection is - // closed and we still get a callback that the handle is being closed. Yay. - // - // We assert that the error is either of these two because we aren't sure - // if any other error values could also indicate this bogus condition, and - // we want to notice if we do something wrong that causes a real error. - DWORD last_error = GetLastError(); - DCHECK(last_error == ERROR_INVALID_HANDLE) << - "Unknown error when closing handle, possibly leaking job"; - if (ERROR_IO_PENDING == last_error) { - SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); - - AsyncResult result; - result.dwError = ERROR_INTERNET_CONNECTION_ABORTED; - result.dwResult = reinterpret_cast<DWORD_PTR>(handle); - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - this, &URLRequestInetJob::CallOnIOComplete, result)); - } - } -} - -// static -HINTERNET URLRequestInetJob::GetTheInternet() { - return the_internet_; -} - -// static -void URLRequestInetJob::InitializeTheInternet(const std::string& user_agent) { - // Hack attack. We are hitting a deadlock in wininet deinitialization. - // What is happening is that when we deinitialize, FreeLibrary will be - // called on wininet. The loader lock is held, and wininet!DllMain is - // called. The problem is that wininet tries to do a bunch of cleanup - // in their DllMain, including calling ICAsyncThread::~ICASyncThread. - // This tries to shutdown the "select thread", and then does a - // WaitForSingleObject on the thread with a 5 sec timeout. However the - // thread they are waiting for cannot exit because the thread shutdown - // routine (LdrShutdownThread) is trying to acquire the loader lock. - // This causes chrome.exe to hang for 5 seconds on shutdown before the - // process will exit. Making sure we close our wininet handles did not help. - // - // Since DLLs are reference counted, we inflate the reference count on - // wininet so that it will never be deinitialized :) - LoadLibraryA("wininet"); - - the_internet_ = InternetOpenA(user_agent.c_str(), - INTERNET_OPEN_TYPE_PRECONFIG, - NULL, // no proxy override - NULL, // no proxy bypass list - INTERNET_FLAG_ASYNC); - InternetSetStatusCallback(the_internet_, URLRequestStatusCallback); - - // Keep track of this message loop so we can catch callers who don't make - // requests on the same thread. Only do this in debug mode; in release mode - // my_message_loop_ doesn't exist. -#ifndef NDEBUG - DCHECK(!my_message_loop_) << "InitializeTheInternet() called twice"; - DCHECK(my_message_loop_ = MessageLoop::current()); -#endif -} - -// static -void CALLBACK URLRequestInetJob::URLRequestStatusCallback( - HINTERNET handle, DWORD_PTR job_id, DWORD status, LPVOID status_info, - DWORD status_info_len) { - switch (status) { - case INTERNET_STATUS_REQUEST_COMPLETE: { - URLRequestInetJob* job = reinterpret_cast<URLRequestInetJob*>(job_id); - - DCHECK(status_info_len == sizeof(AsyncResult)); - AsyncResult* result = static_cast<AsyncResult*>(status_info); - - AutoLock locked(job->loop_lock_); - if (job->loop_) { - job->loop_->PostTask(FROM_HERE, NewRunnableMethod( - job, &URLRequestInetJob::CallOnIOComplete, *result)); - } - break; - } - case INTERNET_STATUS_USER_INPUT_REQUIRED: - case INTERNET_STATUS_STATE_CHANGE: - // TODO(darin): This is probably a security problem. Do something better. - ResumeSuspendedDownload(handle, 0); - break; - } -} diff --git a/net/url_request/url_request_inet_job.h b/net/url_request/url_request_inet_job.h index 60ffcd1..e69de29 100644 --- a/net/url_request/url_request_inet_job.h +++ b/net/url_request/url_request_inet_job.h @@ -1,159 +0,0 @@ -// Copyright (c) 2006-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. - -#ifndef NET_URL_REQUEST_URL_REQUEST_INET_JOB_H__ -#define NET_URL_REQUEST_URL_REQUEST_INET_JOB_H__ - -#include <windows.h> -#include <wininet.h> - -#include "base/lock.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_job.h" - -namespace net { -class AuthData; -} - -class MessageLoop; - -// For all WinInet-based URL requests -class URLRequestInetJob : public URLRequestJob { - public: - URLRequestInetJob(URLRequest* request); - - virtual void SetExtraRequestHeaders(const std::string& headers) { - extra_request_headers_ = headers; - } - - virtual void Kill(); - virtual bool ReadRawData(net::IOBuffer* buf, int buf_size, int *bytes_read); - - // URLRequestJob Authentication methods - virtual void SetAuth(const std::wstring& username, - const std::wstring& password); - virtual void CancelAuth(); - - // A structure holding the result and error code of an asynchronous IO. - // This is a copy of INTERNET_ASYNC_RESULT. - struct AsyncResult { - DWORD_PTR dwResult; - DWORD dwError; - }; - - // A virtual method to handle WinInet callbacks. If this class - // issues asynchronous IO, it will need to override this method - // to receive completions of those asynchronous IOs. The class - // must track whether it has an async IO outstanding, and if it - // does not it must call the base class' OnIOComplete. - virtual void OnIOComplete(const AsyncResult& result) = 0; - - // Used internally to setup the OnIOComplete call. Public because this - // is called from the Windows procedure, and we don't want to make it a - // friend so we can avoid the Windows headers for this header file. - void CallOnIOComplete(const AsyncResult& result); - - HINTERNET request_handle() const { return request_handle_; } - - protected: - virtual ~URLRequestInetJob(); - - // Called by this class and subclasses to send or resend this request. - virtual void SendRequest() = 0; - - // Calls InternetReadFile(Ex) depending on the derived class. - // Returns ERROR_SUCCESS on success, or else a standard Windows error code - // on failure (from GetLastError()). - virtual int CallInternetRead(char* dest, int dest_size, int *bytes_read) = 0; - - // After the base class calls CallInternetRead and the result is available, - // it will call this method to get the number of received bytes. - virtual bool GetReadBytes(const AsyncResult& result, int* bytes_read) = 0; - - // Called by this class and subclasses whenever a WinInet call fails. This - // method returns true if the error just means that we have to wait for - // OnIOComplete to be called. - bool ProcessRequestError(int error); - - // Cleans up the connection, if necessary, and closes the connection and - // request handles. May be called multiple times, it will be a NOP if - // there is nothing to do. - void CleanupConnection(); - - // Closes the given handle. - void CleanupHandle(HINTERNET handle); - - // Returns the global handle to the internet (NOT the same as the connection - // or request handle below) - static HINTERNET GetTheInternet(); - - // Makes appropriate asynch call to re-send a request based on - // dynamic scheme type and user action at authentication prompt - //(OK or Cancel) - virtual void OnCancelAuth() = 0; - virtual void OnSetAuth() = 0; - - // Handle of the connection for this request. This handle is created - // by subclasses that create the connection according to their requirements. - // It will be automatically destroyed by this class when the connection is - // being closed. See also 'request_handle_' - HINTERNET connection_handle_; - - // Handle of the specific request created by subclasses to meet their own - // requirements. This handle has a more narrow scope than the connection - // handle. If non-null, it will be automatically destroyed by this class - // when the connection is being closed. It will be destroyed before the - // connection handle. - HINTERNET request_handle_; - - // The last error that occurred. Used by ContinueDespiteLastError to adjust - // the request's load_flags to ignore this error. - DWORD last_error_; - - // Any extra request headers (\n-delimited) that should be included in the - // request. - std::string extra_request_headers_; - - // Authentication information. - scoped_refptr<net::AuthData> proxy_auth_; - scoped_refptr<net::AuthData> server_auth_; - - private: - - // One-time global state setup - static void InitializeTheInternet(const std::string& user_agent); - - // Runs on some background thread (called by WinInet) - static void CALLBACK URLRequestStatusCallback(HINTERNET handle, - DWORD_PTR job_id, - DWORD status, - LPVOID status_info, - DWORD status_info_len); - - static HINTERNET the_internet_; -#ifndef NDEBUG - static MessageLoop* my_message_loop_; // Used to sanity-check that all - // requests are made on the same - // thread -#endif - - // true if waiting for OnIOComplete to be called - bool is_waiting_; - - // debugging state - is there a read already in progress - bool read_in_progress_; - - // The result and error code of asynchronous IO. It is modified by the - // status callback functions on asynchronous IO completion and passed to - // CallOnIOComplete. Since there is at most one pending IO, the object - // can reuse the async_result_ member for all its asynchronous IOs. - AsyncResult async_result_; - - Lock loop_lock_; - MessageLoop* loop_; - - DISALLOW_EVIL_CONSTRUCTORS(URLRequestInetJob); -}; - -#endif // NET_URL_REQUEST_URL_REQUEST_INET_JOB_H__ diff --git a/net/url_request/url_request_job_manager.cc b/net/url_request/url_request_job_manager.cc index 0b300d1..7cd934e 100644 --- a/net/url_request/url_request_job_manager.cc +++ b/net/url_request/url_request_job_manager.cc @@ -14,11 +14,7 @@ #include "net/url_request/url_request_data_job.h" #include "net/url_request/url_request_error_job.h" #include "net/url_request/url_request_file_job.h" -#if defined(OS_WIN) -#include "net/url_request/url_request_ftp_job.h" -#else #include "net/url_request/url_request_new_ftp_job.h" -#endif #include "net/url_request/url_request_http_job.h" // The built-in set of protocol factories @@ -35,11 +31,7 @@ static const SchemeToFactory kBuiltinFactories[] = { { "http", URLRequestHttpJob::Factory }, { "https", URLRequestHttpJob::Factory }, { "file", URLRequestFileJob::Factory }, -#if defined(OS_WIN) - { "ftp", URLRequestFtpJob::Factory }, -#else { "ftp", URLRequestNewFtpJob::Factory }, -#endif { "about", URLRequestAboutJob::Factory }, { "data", URLRequestDataJob::Factory }, }; |