| Index: net/http/http_transaction_winhttp.cc
|
| ===================================================================
|
| --- net/http/http_transaction_winhttp.cc (revision 7967)
|
| +++ net/http/http_transaction_winhttp.cc (working copy)
|
| @@ -1,1827 +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/http/http_transaction_winhttp.h"
|
| -
|
| -#include <winhttp.h>
|
| -
|
| -#include "base/lock.h"
|
| -#include "base/memory_debug.h"
|
| -#include "base/message_loop.h"
|
| -#include "base/string_piece.h"
|
| -#include "base/string_util.h"
|
| -#include "base/sys_string_conversions.h"
|
| -#include "googleurl/src/gurl.h"
|
| -#include "net/base/auth_cache.h"
|
| -#include "net/base/cert_status_flags.h"
|
| -#include "net/base/dns_resolution_observer.h"
|
| -#include "net/base/load_flags.h"
|
| -#include "net/base/net_errors.h"
|
| -#include "net/base/net_util.h"
|
| -#include "net/base/ssl_config_service.h"
|
| -#include "net/base/upload_data_stream.h"
|
| -#include "net/http/cert_status_cache.h"
|
| -#include "net/http/http_request_info.h"
|
| -#include "net/http/winhttp_request_throttle.h"
|
| -#include "net/proxy/proxy_resolver_winhttp.h"
|
| -
|
| -#pragma comment(lib, "winhttp.lib")
|
| -#pragma warning(disable: 4355)
|
| -
|
| -using base::Time;
|
| -
|
| -namespace net {
|
| -
|
| -static int TranslateOSError(DWORD error) {
|
| - switch (error) {
|
| - case ERROR_SUCCESS:
|
| - return OK;
|
| - case ERROR_FILE_NOT_FOUND:
|
| - return ERR_FILE_NOT_FOUND;
|
| - case ERROR_HANDLE_EOF: // TODO(wtc): return OK?
|
| - return ERR_CONNECTION_CLOSED;
|
| - case ERROR_INVALID_HANDLE:
|
| - return ERR_INVALID_HANDLE;
|
| - case ERROR_INVALID_PARAMETER:
|
| - return ERR_INVALID_ARGUMENT;
|
| -
|
| - case ERROR_WINHTTP_CANNOT_CONNECT:
|
| - return ERR_CONNECTION_FAILED;
|
| - case ERROR_WINHTTP_TIMEOUT:
|
| - return ERR_TIMED_OUT;
|
| - case ERROR_WINHTTP_INVALID_URL:
|
| - return ERR_INVALID_URL;
|
| - case ERROR_WINHTTP_NAME_NOT_RESOLVED:
|
| - return ERR_NAME_NOT_RESOLVED;
|
| - case ERROR_WINHTTP_OPERATION_CANCELLED:
|
| - return ERR_ABORTED;
|
| - case ERROR_WINHTTP_SECURE_CHANNEL_ERROR:
|
| - case ERROR_WINHTTP_SECURE_FAILURE:
|
| - case SEC_E_ILLEGAL_MESSAGE:
|
| - return ERR_SSL_PROTOCOL_ERROR;
|
| - case SEC_E_ALGORITHM_MISMATCH:
|
| - return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
|
| - case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED:
|
| - return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
|
| - case ERROR_WINHTTP_UNRECOGNIZED_SCHEME:
|
| - return ERR_UNKNOWN_URL_SCHEME;
|
| - case ERROR_WINHTTP_INVALID_SERVER_RESPONSE:
|
| - return ERR_INVALID_RESPONSE;
|
| -
|
| - // SSL certificate errors
|
| - case ERROR_WINHTTP_SECURE_CERT_CN_INVALID:
|
| - return ERR_CERT_COMMON_NAME_INVALID;
|
| - case ERROR_WINHTTP_SECURE_CERT_DATE_INVALID:
|
| - return ERR_CERT_DATE_INVALID;
|
| - case ERROR_WINHTTP_SECURE_INVALID_CA:
|
| - return ERR_CERT_AUTHORITY_INVALID;
|
| - case ERROR_WINHTTP_SECURE_CERT_REV_FAILED:
|
| - return ERR_CERT_UNABLE_TO_CHECK_REVOCATION;
|
| - case ERROR_WINHTTP_SECURE_CERT_REVOKED:
|
| - return ERR_CERT_REVOKED;
|
| - case ERROR_WINHTTP_SECURE_INVALID_CERT:
|
| - return ERR_CERT_INVALID;
|
| -
|
| - default:
|
| - DCHECK(error != ERROR_IO_PENDING); // WinHTTP doesn't use this error.
|
| - LOG(WARNING) << "Unknown error " << error
|
| - << " mapped to net::ERR_FAILED";
|
| - return ERR_FAILED;
|
| - }
|
| -}
|
| -
|
| -static int TranslateLastOSError() {
|
| - return TranslateOSError(GetLastError());
|
| -}
|
| -
|
| -// Clear certificate errors that we want to ignore.
|
| -static DWORD FilterSecureFailure(DWORD status, int load_flags) {
|
| - if (load_flags & LOAD_IGNORE_CERT_COMMON_NAME_INVALID)
|
| - status &= ~WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID;
|
| - if (load_flags & LOAD_IGNORE_CERT_DATE_INVALID)
|
| - status &= ~WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID;
|
| - if (load_flags & LOAD_IGNORE_CERT_AUTHORITY_INVALID)
|
| - status &= ~WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA;
|
| - if (load_flags & LOAD_IGNORE_CERT_WRONG_USAGE)
|
| - status &= ~WINHTTP_CALLBACK_STATUS_FLAG_CERT_WRONG_USAGE;
|
| - return status;
|
| -}
|
| -
|
| -static DWORD MapSecureFailureToError(DWORD status) {
|
| - // A certificate may have multiple errors. We report the most
|
| - // serious error.
|
| -
|
| - // Unrecoverable errors
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR)
|
| - return ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT)
|
| - return ERROR_WINHTTP_SECURE_INVALID_CERT;
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED)
|
| - return ERROR_WINHTTP_SECURE_CERT_REVOKED;
|
| -
|
| - // Recoverable errors
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA)
|
| - return ERROR_WINHTTP_SECURE_INVALID_CA;
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)
|
| - return ERROR_WINHTTP_SECURE_CERT_CN_INVALID;
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)
|
| - return ERROR_WINHTTP_SECURE_CERT_DATE_INVALID;
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_WRONG_USAGE)
|
| - return ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE;
|
| -
|
| - // Unknown status. Give it the benefit of the doubt.
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED)
|
| - return ERROR_WINHTTP_SECURE_CERT_REV_FAILED;
|
| -
|
| - // Map a status of 0 to the generic secure failure error. We have seen a
|
| - // case where WinHttp doesn't notify us of a secure failure (so status is 0)
|
| - // before notifying us of a request error with ERROR_WINHTTP_SECURE_FAILURE.
|
| - // (WinInet fails with ERROR_INTERNET_SECURITY_CHANNEL_ERROR in that case.)
|
| - return ERROR_WINHTTP_SECURE_FAILURE;
|
| -}
|
| -
|
| -static int MapSecureFailureToCertStatus(DWORD status) {
|
| - int cert_status = 0;
|
| -
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT)
|
| - cert_status |= CERT_STATUS_INVALID;
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED)
|
| - cert_status |= CERT_STATUS_REVOKED;
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA)
|
| - cert_status |= CERT_STATUS_AUTHORITY_INVALID;
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)
|
| - cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)
|
| - cert_status |= CERT_STATUS_DATE_INVALID;
|
| - if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED)
|
| - cert_status |= CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
|
| -
|
| - return cert_status;
|
| - // TODO(jcampan): what about ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE?
|
| -}
|
| -
|
| -// Session --------------------------------------------------------------------
|
| -
|
| -class HttpTransactionWinHttp::Session
|
| - : public base::RefCounted<HttpTransactionWinHttp::Session> {
|
| - public:
|
| - enum {
|
| - // By default WinHTTP enables only SSL3 and TLS1.
|
| - SECURE_PROTOCOLS_SSL3_TLS1 = WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 |
|
| - WINHTTP_FLAG_SECURE_PROTOCOL_TLS1
|
| - };
|
| -
|
| - explicit Session(ProxyService* proxy_service);
|
| -
|
| - // Opens the primary WinHttp session handle.
|
| - bool Init(const std::string& user_agent);
|
| -
|
| - // Opens the alternative WinHttp session handle for TLS-intolerant servers.
|
| - bool InitNoTLS(const std::string& user_agent);
|
| -
|
| - void AddRefBySessionCallback();
|
| -
|
| - void ReleaseBySessionCallback();
|
| -
|
| - // The primary WinHttp session handle.
|
| - HINTERNET internet() { return internet_; }
|
| -
|
| - // An alternative WinHttp session handle. It is not opened until we have
|
| - // encountered a TLS-intolerant server and used for those servers only.
|
| - // TLS is disabled in this session.
|
| - HINTERNET internet_no_tls() { return internet_no_tls_; }
|
| -
|
| - // The message loop of the thread where the session was created.
|
| - MessageLoop* message_loop() { return message_loop_; }
|
| -
|
| - ProxyService* proxy_service() { return proxy_service_; }
|
| -
|
| - // Gets the HTTP authentication cache for the session.
|
| - AuthCache* auth_cache() { return &auth_cache_; }
|
| -
|
| - HANDLE handle_closing_event() const { return handle_closing_event_; }
|
| -
|
| - CertStatusCache* cert_status_cache() { return &cert_status_cache_; }
|
| -
|
| - bool rev_checking_enabled() const { return rev_checking_enabled_; }
|
| -
|
| - bool tls_enabled() const {
|
| - return (secure_protocols_ & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) != 0;
|
| - }
|
| -
|
| - bool ShouldIgnoreCertRev(const std::string& origin) const {
|
| - OriginSet::const_iterator pos = ignore_cert_rev_servers_.find(origin);
|
| - return pos != ignore_cert_rev_servers_.end();
|
| - }
|
| -
|
| - void IgnoreCertRev(const std::string& origin) {
|
| - ignore_cert_rev_servers_.insert(origin);
|
| - }
|
| -
|
| - WinHttpRequestThrottle* request_throttle() {
|
| - return &request_throttle_;
|
| - }
|
| -
|
| - private:
|
| - friend class base::RefCounted<HttpTransactionWinHttp::Session>;
|
| -
|
| - ~Session();
|
| -
|
| - // Called by the destructor only.
|
| - void WaitUntilCallbacksAllDone();
|
| -
|
| - HINTERNET OpenWinHttpSession(const std::string& user_agent);
|
| -
|
| - // Get the SSL configuration settings and save them in rev_checking_enabled_
|
| - // and secure_protocols_.
|
| - void GetSSLConfig();
|
| -
|
| - HINTERNET internet_;
|
| - HINTERNET internet_no_tls_;
|
| - MessageLoop* message_loop_;
|
| - ProxyService* proxy_service_;
|
| - AuthCache auth_cache_;
|
| -
|
| - // This event object is used when destroying a transaction. It is given
|
| - // to the transaction's session callback if WinHTTP still has the caller's
|
| - // data (request info or read buffer) and we need to wait until WinHTTP is
|
| - // done with the data.
|
| - HANDLE handle_closing_event_;
|
| -
|
| - // The following members ensure a clean destruction of the Session object.
|
| - // The Session destructor waits until all the request handles have been
|
| - // terminated by WinHTTP, at which point no more status callbacks will
|
| - // reference the MessageLoop of the Session.
|
| - //
|
| - // quit_event_ is the event object used for this wait.
|
| - //
|
| - // lock_ protects session_callback_ref_count_ and quitting_.
|
| - //
|
| - // session_callback_ref_count_ is the number of SessionCallback objects
|
| - // that may reference the MessageLoop of the Session.
|
| - //
|
| - // The boolean quitting_ is true when the Session object is being
|
| - // destructed.
|
| - HANDLE quit_event_;
|
| - Lock lock_;
|
| - int session_callback_ref_count_;
|
| - bool quitting_;
|
| -
|
| - // We use a cache to store the certificate error as we cannot always rely on
|
| - // WinHTTP to provide us the SSL error once we restarted a connection asking
|
| - // to ignored errors.
|
| - CertStatusCache cert_status_cache_;
|
| -
|
| - // SSL settings
|
| - bool rev_checking_enabled_;
|
| - DWORD secure_protocols_;
|
| -
|
| - // The servers for which certificate revocation should be ignored.
|
| - //
|
| - // WinHTTP verifies each certificate only once and caches the certificate
|
| - // verification results, so if we ever ignore certificate revocation for a
|
| - // server, we cannot enable revocation checking again for that server for
|
| - // the rest of the session.
|
| - //
|
| - // If we honor changes to the rev_checking_enabled system setting during
|
| - // the session, we will have to remember all the servers we have visited
|
| - // while the rev_checking_enabled setting is false. This will consume a
|
| - // lot of memory. So we now require the users to restart Chrome for a
|
| - // rev_checking_enabled change to take effect, just like IE does.
|
| - typedef std::set<std::string> OriginSet;
|
| - OriginSet ignore_cert_rev_servers_;
|
| -
|
| - WinHttpRequestThrottle request_throttle_;
|
| -};
|
| -
|
| -HttpTransactionWinHttp::Session::Session(ProxyService* proxy_service)
|
| - : internet_(NULL),
|
| - internet_no_tls_(NULL),
|
| - proxy_service_(proxy_service),
|
| - session_callback_ref_count_(0),
|
| - quitting_(false) {
|
| - GetSSLConfig();
|
| -
|
| - // Save the current message loop for callback notifications.
|
| - message_loop_ = MessageLoop::current();
|
| -
|
| - handle_closing_event_ = CreateEvent(NULL,
|
| - FALSE, // auto-reset
|
| - FALSE, // initially nonsignaled
|
| - NULL); // unnamed
|
| -
|
| - quit_event_ = CreateEvent(NULL,
|
| - FALSE, // auto-reset
|
| - FALSE, // initially nonsignaled
|
| - NULL); // unnamed
|
| -}
|
| -
|
| -HttpTransactionWinHttp::Session::~Session() {
|
| - if (internet_) {
|
| - WinHttpCloseHandle(internet_);
|
| - if (internet_no_tls_)
|
| - WinHttpCloseHandle(internet_no_tls_);
|
| -
|
| - // Ensure that all status callbacks that may reference the MessageLoop
|
| - // of this thread are done before we can allow the current thread to exit.
|
| - WaitUntilCallbacksAllDone();
|
| - }
|
| -
|
| - if (handle_closing_event_)
|
| - CloseHandle(handle_closing_event_);
|
| - if (quit_event_)
|
| - CloseHandle(quit_event_);
|
| -}
|
| -
|
| -bool HttpTransactionWinHttp::Session::Init(const std::string& user_agent) {
|
| - DCHECK(!internet_);
|
| -
|
| - internet_ = OpenWinHttpSession(user_agent);
|
| -
|
| - if (!internet_)
|
| - return false;
|
| -
|
| - if (secure_protocols_ != SECURE_PROTOCOLS_SSL3_TLS1) {
|
| - BOOL rv = WinHttpSetOption(internet_, WINHTTP_OPTION_SECURE_PROTOCOLS,
|
| - &secure_protocols_, sizeof(secure_protocols_));
|
| - DCHECK(rv);
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -bool HttpTransactionWinHttp::Session::InitNoTLS(
|
| - const std::string& user_agent) {
|
| - DCHECK(tls_enabled());
|
| - DCHECK(internet_);
|
| - DCHECK(!internet_no_tls_);
|
| -
|
| - internet_no_tls_ = OpenWinHttpSession(user_agent);
|
| -
|
| - if (!internet_no_tls_)
|
| - return false;
|
| -
|
| - DWORD protocols = secure_protocols_ & ~WINHTTP_FLAG_SECURE_PROTOCOL_TLS1;
|
| - BOOL rv = WinHttpSetOption(internet_no_tls_,
|
| - WINHTTP_OPTION_SECURE_PROTOCOLS,
|
| - &protocols, sizeof(protocols));
|
| - DCHECK(rv);
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void HttpTransactionWinHttp::Session::AddRefBySessionCallback() {
|
| - AutoLock lock(lock_);
|
| - session_callback_ref_count_++;
|
| -}
|
| -
|
| -void HttpTransactionWinHttp::Session::ReleaseBySessionCallback() {
|
| - bool need_to_signal;
|
| - {
|
| - AutoLock lock(lock_);
|
| - session_callback_ref_count_--;
|
| - need_to_signal = (quitting_ && session_callback_ref_count_ == 0);
|
| - }
|
| - if (need_to_signal)
|
| - SetEvent(quit_event_);
|
| -}
|
| -
|
| -// This is called by the Session destructor only. By now the transaction
|
| -// factory and all the transactions have been destructed. This means that
|
| -// new transactions can't be created, and existing transactions can't be
|
| -// started, which in turn implies that session_callback_ref_count_ cannot
|
| -// increase. We wait until session_callback_ref_count_ drops to 0.
|
| -void HttpTransactionWinHttp::Session::WaitUntilCallbacksAllDone() {
|
| - bool need_to_wait;
|
| - {
|
| - AutoLock lock(lock_);
|
| - quitting_ = true;
|
| - need_to_wait = (session_callback_ref_count_ != 0);
|
| - }
|
| - if (need_to_wait)
|
| - WaitForSingleObject(quit_event_, INFINITE);
|
| - DCHECK(session_callback_ref_count_ == 0);
|
| -}
|
| -
|
| -HINTERNET HttpTransactionWinHttp::Session::OpenWinHttpSession(
|
| - const std::string& user_agent) {
|
| - // Proxy config will be set explicitly for each request.
|
| - //
|
| - // Although UA string will also be set explicitly for each request, HTTP
|
| - // CONNECT requests use the UA string of the session handle, so we have to
|
| - // pass a UA string to WinHttpOpen.
|
| - HINTERNET internet = WinHttpOpen(ASCIIToWide(user_agent).c_str(),
|
| - WINHTTP_ACCESS_TYPE_NO_PROXY,
|
| - WINHTTP_NO_PROXY_NAME,
|
| - WINHTTP_NO_PROXY_BYPASS,
|
| - WINHTTP_FLAG_ASYNC);
|
| - if (!internet)
|
| - return internet;
|
| -
|
| - // Use a 90-second timeout (1.5 times the default) for connect. Disable
|
| - // name resolution, send, and receive timeouts. We expect our consumer to
|
| - // apply timeouts or provide controls for users to stop requests that are
|
| - // taking too long.
|
| - BOOL rv = WinHttpSetTimeouts(internet, 0, 90000, 0, 0);
|
| - DCHECK(rv);
|
| -
|
| - return internet;
|
| -}
|
| -
|
| -void HttpTransactionWinHttp::Session::GetSSLConfig() {
|
| - SSLConfig ssl_config;
|
| - SSLConfigService::GetSSLConfigNow(&ssl_config);
|
| - rev_checking_enabled_ = ssl_config.rev_checking_enabled;
|
| - secure_protocols_ = 0;
|
| - if (ssl_config.ssl2_enabled)
|
| - secure_protocols_ |= WINHTTP_FLAG_SECURE_PROTOCOL_SSL2;
|
| - if (ssl_config.ssl3_enabled)
|
| - secure_protocols_ |= WINHTTP_FLAG_SECURE_PROTOCOL_SSL3;
|
| - if (ssl_config.tls1_enabled)
|
| - secure_protocols_ |= WINHTTP_FLAG_SECURE_PROTOCOL_TLS1;
|
| -}
|
| -
|
| -// SessionCallback ------------------------------------------------------------
|
| -
|
| -class HttpTransactionWinHttp::SessionCallback
|
| - : public base::RefCountedThreadSafe<HttpTransactionWinHttp::SessionCallback> {
|
| - public:
|
| - SessionCallback(HttpTransactionWinHttp* trans, Session* session)
|
| - : trans_(trans),
|
| - session_(session),
|
| - load_state_(LOAD_STATE_IDLE),
|
| - handle_closing_event_(NULL),
|
| - bytes_available_(0),
|
| - read_buf_(NULL),
|
| - read_buf_len_(0),
|
| - secure_failure_(0),
|
| - connection_was_opened_(false),
|
| - request_was_probably_sent_(false),
|
| - response_was_received_(false),
|
| - response_is_empty_(true) {
|
| - }
|
| -
|
| - // Called when the associated trans_ has to reopen its connection and
|
| - // request handles to recover from certain SSL errors. Resets the members
|
| - // that may have been modified at that point.
|
| - void ResetForNewRequest() {
|
| - secure_failure_ = 0;
|
| - connection_was_opened_ = false;
|
| - }
|
| -
|
| - void DropTransaction() {
|
| - trans_ = NULL;
|
| - }
|
| -
|
| - void Notify(DWORD status, DWORD_PTR result, DWORD error) {
|
| - DWORD secure_failure = 0;
|
| - if (status == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR) {
|
| - switch (error) {
|
| - // WinHttp sends this error code in two interesting cases: 1) when a
|
| - // response header is malformed, and 2) when a response is empty. In
|
| - // the latter case, we want to actually resend the request if the
|
| - // request was sent over a reused "keep-alive" connection. This is a
|
| - // risky thing to do since it is possible that the server did receive
|
| - // our request, but it is unfortunately required to support HTTP keep-
|
| - // alive connections properly, and other browsers all do this too.
|
| - case ERROR_WINHTTP_INVALID_SERVER_RESPONSE:
|
| - if (empty_response_was_received() && !connection_was_opened_)
|
| - error = ERROR_WINHTTP_RESEND_REQUEST;
|
| - break;
|
| - case ERROR_WINHTTP_SECURE_FAILURE:
|
| - secure_failure = secure_failure_;
|
| - break;
|
| - }
|
| - } else if (status == WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE) {
|
| - secure_failure = secure_failure_;
|
| - }
|
| - session_->message_loop()->PostTask(FROM_HERE,
|
| - NewRunnableMethod(this,
|
| - &HttpTransactionWinHttp::SessionCallback::OnNotify,
|
| - status, result, error, secure_failure));
|
| - }
|
| -
|
| - // Calls WinHttpReadData and returns its return value.
|
| - BOOL ReadData(HINTERNET request_handle);
|
| -
|
| - void OnHandleClosing() {
|
| - if (handle_closing_event_)
|
| - SetEvent(handle_closing_event_);
|
| - session_->ReleaseBySessionCallback();
|
| - Release();
|
| - }
|
| -
|
| - // Modified from any thread.
|
| - void set_load_state(LoadState state) {
|
| - load_state_ = state;
|
| - }
|
| - LoadState load_state() const {
|
| - return load_state_;
|
| - }
|
| -
|
| - int bytes_available() const { return bytes_available_; }
|
| - void set_bytes_available(int n) { bytes_available_ = n; }
|
| - void reduce_bytes_available(int n) { bytes_available_ -= n; }
|
| -
|
| - char* read_buf() const { return read_buf_; }
|
| - void set_read_buf(char* buf) { read_buf_ = buf; }
|
| -
|
| - int read_buf_len() const { return read_buf_len_; }
|
| - void set_read_buf_len(int buf_len) { read_buf_len_ = buf_len; }
|
| -
|
| - // Tells this SessionCallback to signal this event when receiving the
|
| - // handle closing status callback.
|
| - void set_handle_closing_event(HANDLE event) {
|
| - handle_closing_event_ = event;
|
| - }
|
| -
|
| - void set_secure_failure(DWORD flags) { secure_failure_ = flags; }
|
| -
|
| - void did_open_connection() {
|
| - connection_was_opened_ = true;
|
| - }
|
| -
|
| - void did_start_sending_request() {
|
| - request_was_probably_sent_ = true;
|
| - }
|
| - bool request_was_probably_sent() const {
|
| - return request_was_probably_sent_;
|
| - }
|
| -
|
| - void did_receive_bytes(DWORD count) {
|
| - response_was_received_ = true;
|
| - if (count)
|
| - response_is_empty_ = false;
|
| - }
|
| -
|
| - private:
|
| - friend base::RefCountedThreadSafe<HttpTransactionWinHttp::SessionCallback>;
|
| - ~SessionCallback() {}
|
| -
|
| - void OnNotify(DWORD status,
|
| - DWORD_PTR result,
|
| - DWORD error,
|
| - DWORD secure_failure) {
|
| - if (trans_)
|
| - trans_->HandleStatusCallback(status, result, error, secure_failure);
|
| -
|
| - // Balances the AddRefs made by the transaction object after an async
|
| - // WinHTTP call.
|
| - Release();
|
| - }
|
| -
|
| - bool empty_response_was_received() const {
|
| - return response_was_received_ && response_is_empty_;
|
| - }
|
| -
|
| - HttpTransactionWinHttp* trans_;
|
| -
|
| - // Session is reference-counted, but this is a plain pointer. The
|
| - // reference on the Session owned by SessionCallback is managed using
|
| - // Session::AddRefBySessionCallback and Session::ReleaseBySessionCallback.
|
| - Session* session_;
|
| -
|
| - // Modified from any thread.
|
| - volatile LoadState load_state_;
|
| -
|
| - // Amount of data available reported by WinHttpQueryDataAvailable that
|
| - // haven't been consumed by WinHttpReadData.
|
| - int bytes_available_;
|
| -
|
| - // Caller's read buffer and buffer size, to be passed to WinHttpReadData.
|
| - // These are used by the IO thread and the thread WinHTTP uses to make
|
| - // status callbacks, but not at the same time.
|
| - char* read_buf_;
|
| - int read_buf_len_;
|
| -
|
| - // If not null, we set this event on receiving the handle closing callback.
|
| - HANDLE handle_closing_event_;
|
| -
|
| - // The secure connection failure flags reported by the
|
| - // WINHTTP_CALLBACK_STATUS_SECURE_FAILURE status callback.
|
| - DWORD secure_failure_;
|
| -
|
| - // True if a connection was opened for this request.
|
| - bool connection_was_opened_;
|
| -
|
| - // True if the request may have been sent to the server (and therefore we
|
| - // should not restart the request).
|
| - bool request_was_probably_sent_;
|
| -
|
| - // True if any response was received.
|
| - bool response_was_received_;
|
| -
|
| - // True if we have an empty response (no headers, no status line, nothing).
|
| - bool response_is_empty_;
|
| -};
|
| -
|
| -BOOL HttpTransactionWinHttp::SessionCallback::ReadData(
|
| - HINTERNET request_handle) {
|
| - DCHECK(bytes_available_ >= 0);
|
| - char* buf = read_buf_;
|
| - read_buf_ = NULL;
|
| - int bytes_to_read = std::min(bytes_available_, read_buf_len_);
|
| - read_buf_len_ = 0;
|
| - if (!bytes_to_read)
|
| - bytes_to_read = 1;
|
| -
|
| - // Because of how WinHTTP fills memory when used asynchronously, Purify isn't
|
| - // able to detect that it's been initialized, so it scans for 0xcd in the
|
| - // buffer and reports UMRs (uninitialized memory reads) for those individual
|
| - // bytes. We override that to avoid the false error reports.
|
| - // See http://b/issue?id=1173916.
|
| - base::MemoryDebug::MarkAsInitialized(buf, bytes_to_read);
|
| - return WinHttpReadData(request_handle, buf, bytes_to_read, NULL);
|
| -}
|
| -
|
| -// static
|
| -void HttpTransactionWinHttp::StatusCallback(HINTERNET handle,
|
| - DWORD_PTR context,
|
| - DWORD status,
|
| - LPVOID status_info,
|
| - DWORD status_info_len) {
|
| - SessionCallback* callback = reinterpret_cast<SessionCallback*>(context);
|
| -
|
| - switch (status) {
|
| - case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
|
| - if (callback)
|
| - callback->OnHandleClosing();
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:
|
| - callback->set_load_state(LOAD_STATE_CONNECTING);
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:
|
| - callback->did_open_connection();
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
|
| - callback->set_load_state(LOAD_STATE_SENDING_REQUEST);
|
| - callback->did_start_sending_request();
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:
|
| - callback->set_load_state(LOAD_STATE_WAITING_FOR_RESPONSE);
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
|
| - callback->did_receive_bytes(*static_cast<DWORD*>(status_info));
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
|
| - DCHECK(callback->bytes_available() == 0);
|
| - DCHECK(status_info_len == sizeof(DWORD));
|
| - callback->set_bytes_available(static_cast<DWORD*>(status_info)[0]);
|
| - if (!callback->ReadData(handle))
|
| - callback->Notify(WINHTTP_CALLBACK_STATUS_REQUEST_ERROR,
|
| - API_READ_DATA, GetLastError());
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
|
| - callback->Notify(status, status_info_len, 0);
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
|
| - DCHECK(status_info_len == sizeof(DWORD));
|
| - callback->Notify(status, static_cast<DWORD*>(status_info)[0], 0);
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
|
| - callback->Notify(status, TRUE, 0);
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
|
| - callback->Notify(status, TRUE, 0);
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: {
|
| - WINHTTP_ASYNC_RESULT* result =
|
| - static_cast<WINHTTP_ASYNC_RESULT*>(status_info);
|
| - callback->Notify(status, result->dwResult, result->dwError);
|
| - if (API_SEND_REQUEST == result->dwResult &&
|
| - ERROR_WINHTTP_NAME_NOT_RESOLVED == result->dwError)
|
| - DidFinishDnsResolutionWithStatus(false,
|
| - GURL(), // null referrer URL.
|
| - reinterpret_cast<void*>(context));
|
| - break;
|
| - }
|
| - // This status callback provides the detailed reason for a secure
|
| - // failure. We map that to an error code and save it for later use.
|
| - case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: {
|
| - DCHECK(status_info_len == sizeof(DWORD));
|
| - DWORD* status_ptr = static_cast<DWORD*>(status_info);
|
| - callback->set_secure_failure(*status_ptr);
|
| - break;
|
| - }
|
| - // Looking up the IP address of a server name. The status_info
|
| - // parameter contains a pointer to the server name being resolved.
|
| - case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME: {
|
| - callback->set_load_state(LOAD_STATE_RESOLVING_HOST);
|
| - std::wstring wname(static_cast<wchar_t*>(status_info),
|
| - status_info_len - 1);
|
| - DidStartDnsResolution(WideToASCII(wname),
|
| - reinterpret_cast<void*>(context));
|
| - break;
|
| - }
|
| - // Successfully found the IP address of the server.
|
| - case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED:
|
| - DidFinishDnsResolutionWithStatus(true,
|
| - GURL(), // null referrer URL.
|
| - reinterpret_cast<void*>(context));
|
| - break;
|
| - }
|
| -}
|
| -
|
| -// Factory --------------------------------------------------------------------
|
| -
|
| -HttpTransactionWinHttp::Factory::~Factory() {
|
| - if (session_)
|
| - session_->Release();
|
| -}
|
| -
|
| -HttpTransaction* HttpTransactionWinHttp::Factory::CreateTransaction() {
|
| - if (is_suspended_)
|
| - return NULL;
|
| -
|
| - if (!session_) {
|
| - session_ = new Session(proxy_service_);
|
| - session_->AddRef();
|
| - }
|
| - return new HttpTransactionWinHttp(session_, proxy_service_->proxy_info());
|
| -}
|
| -
|
| -HttpCache* HttpTransactionWinHttp::Factory::GetCache() {
|
| - return NULL;
|
| -}
|
| -
|
| -void HttpTransactionWinHttp::Factory::Suspend(bool suspend) {
|
| - is_suspended_ = suspend;
|
| -
|
| - if (is_suspended_ && session_) {
|
| - session_->Release();
|
| - session_ = NULL;
|
| - }
|
| -}
|
| -
|
| -// Transaction ----------------------------------------------------------------
|
| -
|
| -HttpTransactionWinHttp::HttpTransactionWinHttp(Session* session,
|
| - const ProxyInfo* info)
|
| - : session_(session),
|
| - request_(NULL),
|
| - load_flags_(0),
|
| - last_error_(ERROR_SUCCESS),
|
| - content_length_remaining_(-1),
|
| - pac_request_(NULL),
|
| - proxy_callback_(this, &HttpTransactionWinHttp::OnProxyInfoAvailable),
|
| - callback_(NULL),
|
| - upload_progress_(0),
|
| - connect_handle_(NULL),
|
| - request_handle_(NULL),
|
| - is_https_(false),
|
| - is_tls_intolerant_(false),
|
| - rev_checking_enabled_(false),
|
| - have_proxy_info_(false),
|
| - need_to_wait_for_handle_closing_(false),
|
| - request_submitted_(false),
|
| - used_embedded_credentials_(false) {
|
| - session->AddRef();
|
| - session_callback_ = new SessionCallback(this, session);
|
| - if (info) {
|
| - proxy_info_.Use(*info);
|
| - have_proxy_info_ = true;
|
| - }
|
| -}
|
| -
|
| -HttpTransactionWinHttp::~HttpTransactionWinHttp() {
|
| - if (pac_request_)
|
| - session_->proxy_service()->CancelPacRequest(pac_request_);
|
| -
|
| - if (request_handle_) {
|
| - if (need_to_wait_for_handle_closing_) {
|
| - session_callback_->set_handle_closing_event(
|
| - session_->handle_closing_event());
|
| - }
|
| - WinHttpCloseHandle(request_handle_);
|
| - if (need_to_wait_for_handle_closing_)
|
| - WaitForSingleObject(session_->handle_closing_event(), INFINITE);
|
| - }
|
| - if (connect_handle_)
|
| - WinHttpCloseHandle(connect_handle_);
|
| -
|
| - if (request_submitted_) {
|
| - session_->request_throttle()->RemoveRequest(connect_peer_,
|
| - request_handle_);
|
| - }
|
| -
|
| - if (session_callback_) {
|
| - session_callback_->DropTransaction();
|
| - session_callback_ = NULL; // Release() reference as side effect.
|
| - }
|
| - if (session_)
|
| - session_->Release();
|
| -}
|
| -
|
| -int HttpTransactionWinHttp::Start(const HttpRequestInfo* request_info,
|
| - CompletionCallback* callback) {
|
| - DCHECK(request_info);
|
| - DCHECK(callback);
|
| -
|
| - // ensure that we only have one asynchronous call at a time.
|
| - DCHECK(!callback_);
|
| -
|
| - LOG(INFO) << request_info->method << ": " << request_info->url;
|
| -
|
| - request_ = request_info;
|
| - load_flags_ = request_info->load_flags;
|
| -
|
| - int rv = OK;
|
| - if (!have_proxy_info_) {
|
| - // Resolve proxy info.
|
| - rv = session_->proxy_service()->ResolveProxy(request_->url,
|
| - &proxy_info_,
|
| - &proxy_callback_,
|
| - &pac_request_);
|
| - if (rv == ERR_IO_PENDING) {
|
| - session_callback_->set_load_state(
|
| - LOAD_STATE_RESOLVING_PROXY_FOR_URL);
|
| - }
|
| - }
|
| -
|
| - if (rv == OK)
|
| - rv = DidResolveProxy(); // calls OpenRequest and SendRequest
|
| -
|
| - if (rv == ERR_IO_PENDING) {
|
| - session_callback_->AddRef(); // balanced when callback runs or from
|
| - // OnProxyInfoAvailable.
|
| - callback_ = callback;
|
| - }
|
| -
|
| - return rv;
|
| -}
|
| -
|
| -int HttpTransactionWinHttp::RestartIgnoringLastError(
|
| - CompletionCallback* callback) {
|
| - int flags = load_flags_;
|
| -
|
| - // Depending on the error, we make different adjustments to our load flags.
|
| - // We DCHECK that we shouldn't already have ignored this error.
|
| - switch (last_error_) {
|
| - case ERROR_WINHTTP_SECURE_CERT_CN_INVALID:
|
| - DCHECK(!(flags & LOAD_IGNORE_CERT_COMMON_NAME_INVALID));
|
| - flags |= LOAD_IGNORE_CERT_COMMON_NAME_INVALID;
|
| - break;
|
| - case ERROR_WINHTTP_SECURE_CERT_DATE_INVALID:
|
| - DCHECK(!(flags & LOAD_IGNORE_CERT_DATE_INVALID));
|
| - flags |= LOAD_IGNORE_CERT_DATE_INVALID;
|
| - break;
|
| - case ERROR_WINHTTP_SECURE_INVALID_CA:
|
| - DCHECK(!(flags & LOAD_IGNORE_CERT_AUTHORITY_INVALID));
|
| - flags |= LOAD_IGNORE_CERT_AUTHORITY_INVALID;
|
| - break;
|
| - case ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE:
|
| - DCHECK(!(flags & LOAD_IGNORE_CERT_WRONG_USAGE));
|
| - flags |= LOAD_IGNORE_CERT_WRONG_USAGE;
|
| - break;
|
| - case ERROR_WINHTTP_SECURE_CERT_REV_FAILED: {
|
| - DCHECK(!(flags & LOAD_IGNORE_CERT_REVOCATION));
|
| - flags |= LOAD_IGNORE_CERT_REVOCATION;
|
| - // WinHTTP doesn't have a SECURITY_FLAG_IGNORE_CERT_REV_FAILED flag
|
| - // and doesn't let us undo WINHTTP_ENABLE_SSL_REVOCATION. The only
|
| - // way to ignore this error is to open a new request without enabling
|
| - // WINHTTP_ENABLE_SSL_REVOCATION.
|
| - if (!ReopenRequest())
|
| - return TranslateLastOSError();
|
| - break;
|
| - }
|
| - // We can't instruct WinHttp to recover from these errors. No choice
|
| - // but to cancel the request.
|
| - case ERROR_WINHTTP_SECURE_CHANNEL_ERROR:
|
| - case ERROR_WINHTTP_SECURE_INVALID_CERT:
|
| - case ERROR_WINHTTP_SECURE_CERT_REVOKED:
|
| - // We don't knows how to continue from here.
|
| - default:
|
| - LOG(ERROR) << "Unable to restart the HTTP transaction ignoring "
|
| - "the error " << last_error_;
|
| - return ERR_ABORTED;
|
| - }
|
| -
|
| - // Update the load flags to ignore the specified error.
|
| - load_flags_ = flags;
|
| -
|
| - return Restart(callback);
|
| -}
|
| -
|
| -int HttpTransactionWinHttp::RestartWithAuth(
|
| - const std::wstring& username,
|
| - const std::wstring& password,
|
| - CompletionCallback* callback) {
|
| - DCHECK(proxy_auth_ && proxy_auth_->state == AUTH_STATE_NEED_AUTH ||
|
| - server_auth_ && server_auth_->state == AUTH_STATE_NEED_AUTH);
|
| -
|
| - // Proxy gets set first, then WWW.
|
| - AuthData* auth =
|
| - proxy_auth_ && proxy_auth_->state == AUTH_STATE_NEED_AUTH ?
|
| - proxy_auth_ : server_auth_;
|
| -
|
| - if (auth) {
|
| - auth->state = AUTH_STATE_HAVE_AUTH;
|
| - auth->username = username;
|
| - auth->password = password;
|
| - }
|
| -
|
| - return Restart(callback);
|
| -}
|
| -
|
| -// The code common to RestartIgnoringLastError and RestartWithAuth.
|
| -int HttpTransactionWinHttp::Restart(CompletionCallback* callback) {
|
| - DCHECK(callback);
|
| -
|
| - // ensure that we only have one asynchronous call at a time.
|
| - DCHECK(!callback_);
|
| -
|
| - int rv = RestartInternal();
|
| - if (rv != ERR_IO_PENDING)
|
| - return rv;
|
| -
|
| - session_callback_->AddRef(); // balanced when callback runs.
|
| -
|
| - callback_ = callback;
|
| - return ERR_IO_PENDING;
|
| -}
|
| -
|
| -// If HttpTransactionWinHttp needs to restart itself after handling an error,
|
| -// it calls this method. This method leaves callback_ unchanged. The caller
|
| -// is responsible for calling session_callback_->AddRef() if this method
|
| -// returns ERR_IO_PENDING.
|
| -int HttpTransactionWinHttp::RestartInternal() {
|
| - content_length_remaining_ = -1;
|
| - upload_progress_ = 0;
|
| -
|
| - return SendRequest();
|
| -}
|
| -
|
| -// We use WinHttpQueryDataAvailable rather than pure async read to trade
|
| -// a better latency for a decreased throughput. We'll make more IO calls,
|
| -// and thus use more CPU for a given transaction by using
|
| -// WinHttpQueryDataAvailable, but it allows us to get a faster response
|
| -// time to the app for data, which is more important.
|
| -int HttpTransactionWinHttp::Read(char* buf, int buf_len,
|
| - CompletionCallback* callback) {
|
| - DCHECK(buf);
|
| - DCHECK(buf_len > 0);
|
| - DCHECK(callback);
|
| -
|
| - DCHECK(!callback_);
|
| - DCHECK(request_handle_);
|
| -
|
| - // If we have already received the full response, then we know we are done.
|
| - if (content_length_remaining_ == 0) {
|
| - LogTransactionMetrics();
|
| - return 0;
|
| - }
|
| -
|
| - session_callback_->set_read_buf(buf);
|
| - session_callback_->set_read_buf_len(buf_len);
|
| -
|
| - // We must consume all the available data reported by the previous
|
| - // WinHttpQueryDataAvailable call before we can call
|
| - // WinHttpQueryDataAvailable again.
|
| - BOOL ok;
|
| - if (session_callback_->bytes_available()) {
|
| - ok = session_callback_->ReadData(request_handle_);
|
| - } else {
|
| - ok = WinHttpQueryDataAvailable(request_handle_, NULL);
|
| - }
|
| - if (!ok)
|
| - return TranslateLastOSError();
|
| -
|
| - session_callback_->set_load_state(LOAD_STATE_READING_RESPONSE);
|
| - session_callback_->AddRef(); // balanced when callback runs.
|
| - need_to_wait_for_handle_closing_ = true;
|
| -
|
| - callback_ = callback;
|
| - return ERR_IO_PENDING;
|
| -}
|
| -
|
| -const HttpResponseInfo* HttpTransactionWinHttp::GetResponseInfo() const {
|
| - return (response_.headers || response_.ssl_info.cert) ? &response_ : NULL;
|
| -}
|
| -
|
| -LoadState HttpTransactionWinHttp::GetLoadState() const {
|
| - return session_callback_->load_state();
|
| -}
|
| -
|
| -uint64 HttpTransactionWinHttp::GetUploadProgress() const {
|
| - return upload_progress_;
|
| -}
|
| -
|
| -void HttpTransactionWinHttp::DoCallback(int rv) {
|
| - DCHECK(rv != ERR_IO_PENDING);
|
| - DCHECK(callback_);
|
| -
|
| - // since Run may result in Read being called, clear callback_ up front.
|
| - CompletionCallback* c = callback_;
|
| - callback_ = NULL;
|
| - c->Run(rv);
|
| -}
|
| -
|
| -bool HttpTransactionWinHttp::OpenRequest() {
|
| - DCHECK(!connect_handle_);
|
| - DCHECK(!request_handle_);
|
| -
|
| - const GURL& url = request_->url;
|
| - const std::string& scheme = url.scheme();
|
| -
|
| - // Flags passed to WinHttpOpenRequest. Disable any conversion WinHttp
|
| - // might perform on our URL string. We handle the escaping ourselves.
|
| - DWORD open_flags = WINHTTP_FLAG_ESCAPE_DISABLE |
|
| - WINHTTP_FLAG_ESCAPE_DISABLE_QUERY |
|
| - WINHTTP_FLAG_NULL_CODEPAGE;
|
| -
|
| - // We should only be dealing with HTTP at this point:
|
| - DCHECK(LowerCaseEqualsASCII(scheme, "http") ||
|
| - LowerCaseEqualsASCII(scheme, "https"));
|
| -
|
| - int in_port = url.IntPort();
|
| - DCHECK(in_port != url_parse::PORT_INVALID) <<
|
| - "Valid URLs should have valid ports";
|
| -
|
| - // Map to port numbers that Windows expects.
|
| - INTERNET_PORT port = in_port;
|
| - if (LowerCaseEqualsASCII(scheme, "https")) {
|
| - is_https_ = true;
|
| - open_flags |= WINHTTP_FLAG_SECURE;
|
| - if (in_port == url_parse::PORT_UNSPECIFIED)
|
| - port = INTERNET_DEFAULT_HTTPS_PORT;
|
| - } else {
|
| - if (in_port == url_parse::PORT_UNSPECIFIED)
|
| - port = INTERNET_DEFAULT_HTTP_PORT;
|
| - }
|
| -
|
| - const std::string& host = url.host();
|
| -
|
| - // Use the primary session handle unless we are talking to a TLS-intolerant
|
| - // server.
|
| - //
|
| - // Since the SSL protocol versions enabled are an option of a session
|
| - // handle, supporting TLS-intolerant servers unfortunately requires opening
|
| - // an alternative session in which TLS 1.0 is disabled.
|
| - if (!session_->internet() && !session_->Init(request_->user_agent)) {
|
| - DLOG(ERROR) << "unable to create the internet";
|
| - return false;
|
| - }
|
| - HINTERNET internet = session_->internet();
|
| - if (is_tls_intolerant_) {
|
| - if (!session_->internet_no_tls() &&
|
| - !session_->InitNoTLS(request_->user_agent)) {
|
| - DLOG(ERROR) << "unable to create the no-TLS alternative internet";
|
| - return false;
|
| - }
|
| - internet = session_->internet_no_tls();
|
| - }
|
| -
|
| - // This function operates synchronously.
|
| - connect_handle_ =
|
| - WinHttpConnect(internet, ASCIIToWide(host).c_str(), port, 0);
|
| - if (!connect_handle_) {
|
| - DLOG(ERROR) << "WinHttpConnect failed: " << GetLastError();
|
| - return false;
|
| - }
|
| -
|
| - std::string request_path = url.PathForRequest();
|
| -
|
| - // This function operates synchronously.
|
| - request_handle_ =
|
| - WinHttpOpenRequest(connect_handle_,
|
| - ASCIIToWide(request_->method).c_str(),
|
| - ASCIIToWide(request_path).c_str(),
|
| - NULL, // use HTTP/1.1
|
| - WINHTTP_NO_REFERER, // none
|
| - WINHTTP_DEFAULT_ACCEPT_TYPES, // none
|
| - open_flags);
|
| - if (!request_handle_) {
|
| - DLOG(ERROR) << "WinHttpOpenRequest failed: " << GetLastError();
|
| - return false;
|
| - }
|
| -
|
| - // TODO(darin): we may wish to prune-back the set of notifications we receive
|
| - WINHTTP_STATUS_CALLBACK old_callback = WinHttpSetStatusCallback(
|
| - request_handle_, StatusCallback,
|
| - WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL);
|
| - DCHECK(old_callback == NULL);
|
| - if (old_callback == WINHTTP_INVALID_STATUS_CALLBACK) {
|
| - DLOG(ERROR) << "WinHttpSetStatusCallback failed:" << GetLastError();
|
| - return false;
|
| - }
|
| -
|
| - DWORD_PTR ctx = reinterpret_cast<DWORD_PTR>(session_callback_.get());
|
| - if (!WinHttpSetOption(request_handle_, WINHTTP_OPTION_CONTEXT_VALUE,
|
| - &ctx, sizeof(ctx))) {
|
| - DLOG(ERROR) << "WinHttpSetOption context value failed:" << GetLastError();
|
| - return false;
|
| - }
|
| -
|
| - // We just associated a status callback context value with the request
|
| - // handle.
|
| - session_callback_->AddRef(); // balanced in OnHandleClosing
|
| - session_->AddRefBySessionCallback();
|
| -
|
| - // We have our own cookie and redirect management.
|
| - DWORD options = WINHTTP_DISABLE_COOKIES |
|
| - WINHTTP_DISABLE_REDIRECTS;
|
| -
|
| - if (!WinHttpSetOption(request_handle_, WINHTTP_OPTION_DISABLE_FEATURE,
|
| - &options, sizeof(options))) {
|
| - DLOG(ERROR) << "WinHttpSetOption disable feature failed:" << GetLastError();
|
| - return false;
|
| - }
|
| -
|
| - // Disable auto-login for Negotiate and NTLM auth methods.
|
| - DWORD security_level = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
|
| - if (!WinHttpSetOption(request_handle_, WINHTTP_OPTION_AUTOLOGON_POLICY,
|
| - &security_level, sizeof(security_level))) {
|
| - DLOG(ERROR) << "WinHttpSetOption autologon failed: " << GetLastError();
|
| - return false;
|
| - }
|
| -
|
| - // Add request headers. WinHttp is known to convert the headers to bytes
|
| - // using the system charset converter, so we use the same converter to map
|
| - // our request headers to UTF-16 before handing the data to WinHttp.
|
| - std::wstring request_headers = base::SysNativeMBToWide(GetRequestHeaders());
|
| -
|
| - DWORD len = static_cast<DWORD>(request_headers.size());
|
| - if (!WinHttpAddRequestHeaders(request_handle_,
|
| - request_headers.c_str(),
|
| - len,
|
| - WINHTTP_ADDREQ_FLAG_ADD |
|
| - WINHTTP_ADDREQ_FLAG_REPLACE)) {
|
| - DLOG(ERROR) << "WinHttpAddRequestHeaders failed: " << GetLastError();
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -int HttpTransactionWinHttp::SendRequest() {
|
| - DCHECK(request_handle_);
|
| -
|
| - // Apply any authentication (username/password) we might have.
|
| - ApplyAuth();
|
| -
|
| - // Apply any proxy info.
|
| - proxy_info_.Apply(request_handle_);
|
| -
|
| - // Check SSL server certificate revocation.
|
| - if (is_https_) {
|
| - bool ignore_cert_rev = (load_flags_ & LOAD_IGNORE_CERT_REVOCATION) != 0;
|
| - GURL origin = request_->url.GetOrigin();
|
| - const std::string& origin_spec = origin.spec();
|
| - if (ignore_cert_rev)
|
| - session_->IgnoreCertRev(origin_spec);
|
| - else if (session_->ShouldIgnoreCertRev(origin_spec))
|
| - ignore_cert_rev = true;
|
| -
|
| - if (session_->rev_checking_enabled() && !ignore_cert_rev) {
|
| - DWORD options = WINHTTP_ENABLE_SSL_REVOCATION;
|
| - if (!WinHttpSetOption(request_handle_, WINHTTP_OPTION_ENABLE_FEATURE,
|
| - &options, sizeof(options))) {
|
| - DLOG(ERROR) << "WinHttpSetOption failed: " << GetLastError();
|
| - return TranslateLastOSError();
|
| - }
|
| - rev_checking_enabled_ = true;
|
| - }
|
| - }
|
| -
|
| - const int kCertFlags = LOAD_IGNORE_CERT_COMMON_NAME_INVALID |
|
| - LOAD_IGNORE_CERT_DATE_INVALID |
|
| - LOAD_IGNORE_CERT_AUTHORITY_INVALID |
|
| - LOAD_IGNORE_CERT_WRONG_USAGE;
|
| -
|
| - if (load_flags_ & kCertFlags) {
|
| - DWORD security_flags;
|
| - DWORD length = sizeof(security_flags);
|
| -
|
| - if (!WinHttpQueryOption(request_handle_,
|
| - WINHTTP_OPTION_SECURITY_FLAGS,
|
| - &security_flags,
|
| - &length)) {
|
| - NOTREACHED() << "WinHttpQueryOption failed.";
|
| - return TranslateLastOSError();
|
| - }
|
| -
|
| - // On Vista, WinHttpSetOption() fails with an incorrect parameter error.
|
| - // WinHttpQueryOption() sets an undocumented flag (0x01000000, which seems
|
| - // to be a query-only flag) in security_flags that causes this error. To
|
| - // work-around it, we only keep the documented error flags.
|
| - security_flags &= (SECURITY_FLAG_IGNORE_UNKNOWN_CA |
|
| - SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
|
| - SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
|
| - SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE);
|
| -
|
| - if (load_flags_ & LOAD_IGNORE_CERT_COMMON_NAME_INVALID)
|
| - security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
|
| -
|
| - if (load_flags_ & LOAD_IGNORE_CERT_DATE_INVALID)
|
| - security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
|
| -
|
| - if (load_flags_ & LOAD_IGNORE_CERT_AUTHORITY_INVALID)
|
| - security_flags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
|
| -
|
| - if (load_flags_ & LOAD_IGNORE_CERT_WRONG_USAGE)
|
| - security_flags |= SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
|
| -
|
| - if (!WinHttpSetOption(request_handle_,
|
| - WINHTTP_OPTION_SECURITY_FLAGS,
|
| - &security_flags,
|
| - sizeof(security_flags))) {
|
| - NOTREACHED() << "WinHttpSetOption failed.";
|
| - return TranslateLastOSError();
|
| - }
|
| - }
|
| -
|
| - response_.request_time = Time::Now();
|
| - response_.was_cached = false;
|
| -
|
| - DWORD total_size = 0;
|
| - if (request_->upload_data) {
|
| - upload_stream_.reset(new UploadDataStream(request_->upload_data));
|
| - uint64 upload_len = upload_stream_->size();
|
| - if (upload_len == 0) {
|
| - upload_stream_.reset();
|
| - } else {
|
| - // TODO(darin): no way to support >4GB uploads w/ WinHttp?
|
| - if (upload_len > static_cast<uint64>(DWORD(-1))) {
|
| - NOTREACHED() << "upload length is too large";
|
| - return ERR_FILE_TOO_BIG;
|
| - }
|
| -
|
| - total_size = static_cast<DWORD>(upload_len);
|
| - }
|
| - }
|
| -
|
| - if (request_submitted_) {
|
| - request_submitted_ = false;
|
| - session_->request_throttle()->NotifyRequestDone(connect_peer_);
|
| - }
|
| - if (proxy_info_.is_direct())
|
| - connect_peer_ = request_->url.GetOrigin().spec();
|
| - else
|
| - connect_peer_ = proxy_info_.proxy_server();
|
| - DWORD_PTR ctx = reinterpret_cast<DWORD_PTR>(session_callback_.get());
|
| - if (!session_->request_throttle()->SubmitRequest(connect_peer_,
|
| - request_handle_,
|
| - total_size, ctx)) {
|
| - last_error_ = GetLastError();
|
| - DLOG(ERROR) << "WinHttpSendRequest failed: " << last_error_;
|
| - return TranslateOSError(last_error_);
|
| - }
|
| -
|
| - request_submitted_ = true;
|
| - return ERR_IO_PENDING;
|
| -}
|
| -
|
| -// Called after certain failures of SendRequest to reset the members opened
|
| -// or modified in OpenRequest and SendRequest and call OpenRequest again.
|
| -bool HttpTransactionWinHttp::ReopenRequest() {
|
| - DCHECK(connect_handle_);
|
| - DCHECK(request_handle_);
|
| -
|
| - session_callback_->set_handle_closing_event(
|
| - session_->handle_closing_event());
|
| - WinHttpCloseHandle(request_handle_);
|
| - WaitForSingleObject(session_->handle_closing_event(), INFINITE);
|
| - request_handle_ = NULL;
|
| - WinHttpCloseHandle(connect_handle_);
|
| - connect_handle_ = NULL;
|
| - session_callback_->ResetForNewRequest();
|
| -
|
| - // Don't need to reset is_https_, rev_checking_enabled_, and
|
| - // response_.request_time.
|
| -
|
| - return OpenRequest();
|
| -}
|
| -
|
| -int HttpTransactionWinHttp::DidResolveProxy() {
|
| - // We may already have a request handle if we are changing proxy config.
|
| - if (!(request_handle_ ? ReopenRequest() : OpenRequest()))
|
| - return TranslateLastOSError();
|
| -
|
| - return SendRequest();
|
| -}
|
| -
|
| -int HttpTransactionWinHttp::DidReceiveError(DWORD error,
|
| - DWORD secure_failure) {
|
| - DCHECK(error != ERROR_SUCCESS);
|
| -
|
| - session_callback_->set_load_state(LOAD_STATE_IDLE);
|
| - need_to_wait_for_handle_closing_ = false;
|
| -
|
| - int rv;
|
| -
|
| - if (error == ERROR_WINHTTP_RESEND_REQUEST)
|
| - return RestartInternal();
|
| -
|
| - if (error == ERROR_WINHTTP_NAME_NOT_RESOLVED ||
|
| - error == ERROR_WINHTTP_CANNOT_CONNECT ||
|
| - error == ERROR_WINHTTP_TIMEOUT) {
|
| - // These errors may have been caused by a proxy configuration error, or
|
| - // rather they may go away by trying a different proxy config! If we have
|
| - // an explicit proxy config, then we just have to report an error.
|
| - if (!have_proxy_info_) {
|
| - rv = session_->proxy_service()->ReconsiderProxyAfterError(
|
| - request_->url, &proxy_info_, &proxy_callback_, &pac_request_);
|
| - if (rv == OK) // got new proxy info to try
|
| - return DidResolveProxy();
|
| - if (rv == ERR_IO_PENDING) // waiting to resolve proxy info
|
| - return rv;
|
| - // else, fall through and just report an error.
|
| - }
|
| - }
|
| -
|
| - if (error == ERROR_WINHTTP_SECURE_FAILURE) {
|
| - DWORD filtered_secure_failure = FilterSecureFailure(secure_failure,
|
| - load_flags_);
|
| - // If load_flags_ ignores all the errors in secure_failure, we shouldn't
|
| - // get the ERROR_WINHTTP_SECURE_FAILURE error.
|
| - DCHECK(filtered_secure_failure || !secure_failure);
|
| - error = MapSecureFailureToError(filtered_secure_failure);
|
| - }
|
| -
|
| - last_error_ = error;
|
| - rv = TranslateOSError(error);
|
| -
|
| - if ((rv == ERR_SSL_PROTOCOL_ERROR ||
|
| - rv == ERR_SSL_VERSION_OR_CIPHER_MISMATCH) &&
|
| - !session_callback_->request_was_probably_sent() &&
|
| - session_->tls_enabled() && !is_tls_intolerant_) {
|
| - // The server might be TLS intolerant. Or it might be an SSL 3.0 server
|
| - // that chose a TLS-only cipher suite, which we handle in the same way.
|
| - // Downgrade to SSL 3.0 and retry.
|
| - is_tls_intolerant_ = true;
|
| - if (!ReopenRequest())
|
| - return TranslateLastOSError();
|
| - return RestartInternal();
|
| - }
|
| - if (rv == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
|
| - // TODO(wtc): Bug 1230409: We don't support SSL client authentication yet.
|
| - // For now we set a null client certificate, which works on XP SP3, Vista
|
| - // and later. On XP SP2 and below, this fails with ERROR_INVALID_PARAMETER
|
| - // (87). This allows us to access servers that request but do not require
|
| - // client certificates.
|
| - if (WinHttpSetOption(request_handle_,
|
| - WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
|
| - WINHTTP_NO_CLIENT_CERT_CONTEXT, 0))
|
| - return RestartInternal();
|
| - }
|
| - if (IsCertificateError(rv)) {
|
| - response_.ssl_info.cert = GetServerCertificate();
|
| - response_.ssl_info.cert_status =
|
| - MapSecureFailureToCertStatus(secure_failure);
|
| - CertStatusCache* cert_status_cache = session_->cert_status_cache();
|
| - cert_status_cache->SetCertStatus(*response_.ssl_info.cert,
|
| - request_->url.host(),
|
| - response_.ssl_info.cert_status);
|
| - }
|
| -
|
| - return rv;
|
| -}
|
| -
|
| -int HttpTransactionWinHttp::DidSendRequest() {
|
| - BOOL ok;
|
| - if (upload_stream_.get() && upload_stream_->buf_len() > 0) {
|
| - // write upload data
|
| - DWORD buf_len = static_cast<DWORD>(upload_stream_->buf_len());
|
| - ok = WinHttpWriteData(request_handle_,
|
| - upload_stream_->buf(),
|
| - buf_len,
|
| - NULL);
|
| - if (ok)
|
| - need_to_wait_for_handle_closing_ = true;
|
| - } else {
|
| - upload_stream_.reset();
|
| - need_to_wait_for_handle_closing_ = false;
|
| -
|
| - // begin receiving the response
|
| - ok = WinHttpReceiveResponse(request_handle_, NULL);
|
| - }
|
| - return ok ? ERR_IO_PENDING : TranslateLastOSError();
|
| -}
|
| -
|
| -int HttpTransactionWinHttp::DidWriteData(DWORD num_bytes) {
|
| - DCHECK(upload_stream_.get());
|
| - DCHECK(num_bytes > 0);
|
| -
|
| - upload_stream_->DidConsume(num_bytes);
|
| - upload_progress_ = upload_stream_->position();
|
| -
|
| - // OK, we are ready to start receiving the response. The code in
|
| - // DidSendRequest does exactly what we want!
|
| - return DidSendRequest();
|
| -}
|
| -
|
| -int HttpTransactionWinHttp::DidReadData(DWORD num_bytes) {
|
| - int rv = static_cast<int>(num_bytes);
|
| - DCHECK(rv >= 0);
|
| -
|
| - session_callback_->set_load_state(LOAD_STATE_IDLE);
|
| - session_callback_->reduce_bytes_available(rv);
|
| - need_to_wait_for_handle_closing_ = false;
|
| -
|
| - if (content_length_remaining_ > 0) {
|
| - content_length_remaining_ -= rv;
|
| -
|
| - // HTTP/1.0 servers are known to send more data than they report in their
|
| - // Content-Length header (in the non-keepalive case). IE and Moz both
|
| - // tolerate this situation, and therefore so must we.
|
| - if (content_length_remaining_ < 0)
|
| - content_length_remaining_ = 0;
|
| - }
|
| -
|
| - // We have read the entire response. Mark the request done to unblock a
|
| - // queued request.
|
| - if (rv == 0) {
|
| - LogTransactionMetrics();
|
| - DCHECK(request_submitted_);
|
| - request_submitted_ = false;
|
| - session_->request_throttle()->NotifyRequestDone(connect_peer_);
|
| - }
|
| -
|
| - return rv;
|
| -}
|
| -
|
| -void HttpTransactionWinHttp::LogTransactionMetrics() const {
|
| - base::TimeDelta duration = base::Time::Now() - response_.request_time;
|
| - if (60 < duration.InMinutes())
|
| - return;
|
| - UMA_HISTOGRAM_LONG_TIMES(L"Net.Transaction_Latency_WinHTTP", duration);
|
| -}
|
| -
|
| -int HttpTransactionWinHttp::DidReceiveHeaders() {
|
| - session_callback_->set_load_state(LOAD_STATE_IDLE);
|
| -
|
| - DWORD size = 0;
|
| - if (!WinHttpQueryHeaders(request_handle_,
|
| - WINHTTP_QUERY_RAW_HEADERS,
|
| - WINHTTP_HEADER_NAME_BY_INDEX,
|
| - NULL,
|
| - &size,
|
| - WINHTTP_NO_HEADER_INDEX)) {
|
| - DWORD error = GetLastError();
|
| - if (error != ERROR_INSUFFICIENT_BUFFER) {
|
| - DLOG(ERROR) << "WinHttpQueryHeaders failed: " << GetLastError();
|
| - return TranslateLastOSError();
|
| - }
|
| - // OK, size should tell us how much to allocate...
|
| - DCHECK(size > 0);
|
| - }
|
| -
|
| - std::wstring raw_headers;
|
| -
|
| - // 'size' is the number of bytes rather than the number of characters.
|
| - DCHECK(size % 2 == 0);
|
| - if (!WinHttpQueryHeaders(request_handle_,
|
| - WINHTTP_QUERY_RAW_HEADERS,
|
| - WINHTTP_HEADER_NAME_BY_INDEX,
|
| - WriteInto(&raw_headers, size/2 + 1),
|
| - &size,
|
| - WINHTTP_NO_HEADER_INDEX)) {
|
| - DLOG(ERROR) << "WinHttpQueryHeaders failed: " << GetLastError();
|
| - return TranslateLastOSError();
|
| - }
|
| -
|
| - response_.response_time = Time::Now();
|
| -
|
| - // From experimentation, it appears that WinHttp translates non-ASCII bytes
|
| - // found in the response headers to UTF-16 assuming that they are encoded
|
| - // using the default system charset. We attempt to undo that here.
|
| - response_.headers =
|
| - new HttpResponseHeaders(base::SysWideToNativeMB(raw_headers));
|
| -
|
| - // WinHTTP truncates a response longer than 2GB. Perhaps it stores the
|
| - // response's content length in a signed 32-bit integer. We fail rather
|
| - // than reading a truncated response.
|
| - if (response_.headers->GetContentLength() > 0x80000000)
|
| - return ERR_FILE_TOO_BIG;
|
| -
|
| - response_.vary_data.Init(*request_, *response_.headers);
|
| - int rv = PopulateAuthChallenge();
|
| - if (rv != OK)
|
| - return rv;
|
| -
|
| - // Unfortunately, WinHttp does not close the connection when a non-keepalive
|
| - // response is _not_ followed by the server closing the connection. So, we
|
| - // attempt to hack around this bug.
|
| - if (!response_.headers->IsKeepAlive())
|
| - content_length_remaining_ = response_.headers->GetContentLength();
|
| -
|
| - return OK;
|
| -}
|
| -
|
| -// Populates response_.auth_challenge with the authentication challenge info.
|
| -int HttpTransactionWinHttp::PopulateAuthChallenge() {
|
| - DCHECK(response_.headers);
|
| -
|
| - int status = response_.headers->response_code();
|
| - if (status != 401 && status != 407)
|
| - return OK;
|
| -
|
| - scoped_refptr<AuthChallengeInfo> auth_info = new AuthChallengeInfo;
|
| -
|
| - auth_info->is_proxy = (status == 407);
|
| -
|
| - if (auth_info->is_proxy) {
|
| - // TODO(wtc): get the proxy server host from proxy_info_.
|
| - // TODO(wtc): internationalize?
|
| - auth_info->host = L"proxy";
|
| - } else {
|
| - auth_info->host = ASCIIToWide(request_->url.host());
|
| - }
|
| -
|
| - // Here we're checking only the first *-Authenticate header. When a server
|
| - // responds with multiple methods, we use the first.
|
| - // TODO(wtc): Bug 1124614: look at all the authentication methods and pick
|
| - // the best one that we support. failover to other authentication methods.
|
| - std::string header_value;
|
| - std::string header_name = auth_info->is_proxy ?
|
| - "Proxy-Authenticate" : "WWW-Authenticate";
|
| - if (!response_.headers->EnumerateHeader(NULL, header_name, &header_value))
|
| - return OK;
|
| -
|
| - // TODO(darin): Need to support RFC 2047 encoded realm strings. For now, we
|
| - // limit our support to ASCII and "native code page" realm strings.
|
| - std::wstring auth_header = base::SysNativeMBToWide(header_value);
|
| -
|
| - // auth_header is a string which looks like:
|
| - // Digest realm="The Awesome Site", domain="/page.html", ...
|
| - std::wstring::const_iterator space = find(auth_header.begin(),
|
| - auth_header.end(), L' ');
|
| - auth_info->scheme.assign(auth_header.begin(), space);
|
| - auth_info->realm = GetHeaderParamValue(auth_header, L"realm");
|
| -
|
| - // Now auth_info has been fully populated. Before we swap it with
|
| - // response_.auth_challenge, update the auth cache key and remove any
|
| - // presumably incorrect auth data in the auth cache.
|
| - std::string* auth_cache_key;
|
| - AuthData* auth;
|
| - if (auth_info->is_proxy) {
|
| - if (!proxy_auth_)
|
| - proxy_auth_ = new AuthData;
|
| - auth = proxy_auth_;
|
| - auth_cache_key = &proxy_auth_cache_key_;
|
| - } else {
|
| - if (!server_auth_)
|
| - server_auth_ = new AuthData;
|
| - auth = server_auth_;
|
| - auth_cache_key = &server_auth_cache_key_;
|
| - }
|
| - *auth_cache_key = AuthCache::HttpKey(request_->url, *auth_info);
|
| - DCHECK(!auth_cache_key->empty());
|
| - auth->scheme = auth_info->scheme;
|
| - if (auth->state == AUTH_STATE_HAVE_AUTH) {
|
| - // Remove the cache entry for the credentials we just failed on.
|
| - // Note: we require the username/password to match before removing
|
| - // since the entry in the cache may be newer than what we used last time.
|
| - AuthData* cached_auth = session_->auth_cache()->Lookup(*auth_cache_key);
|
| - if (cached_auth && cached_auth->username == auth->username &&
|
| - cached_auth->password == auth->password)
|
| - session_->auth_cache()->Remove(*auth_cache_key);
|
| - auth->state = AUTH_STATE_NEED_AUTH;
|
| - }
|
| - DCHECK(auth->state == AUTH_STATE_NEED_AUTH);
|
| -
|
| - // Try to use the username/password embedded in the URL first.
|
| - // (By checking !used_embedded_credentials_, we make sure that this
|
| - // is only done once for the transaction.)
|
| - if (!auth_info->is_proxy && request_->url.has_username() &&
|
| - !used_embedded_credentials_) {
|
| - // TODO(wtc) It may be necessary to unescape the username and password
|
| - // after extracting them from the URL. We should be careful about
|
| - // embedded nulls in that case.
|
| - used_embedded_credentials_ = true;
|
| - auth->state = AUTH_STATE_HAVE_AUTH;
|
| - auth->username = ASCIIToWide(request_->url.username());
|
| - auth->password = ASCIIToWide(request_->url.password());
|
| - return RestartInternal();
|
| - }
|
| -
|
| - // Check the auth cache for an entry.
|
| - AuthData* cached_auth = session_->auth_cache()->Lookup(*auth_cache_key);
|
| - if (cached_auth) {
|
| - auth->state = AUTH_STATE_HAVE_AUTH;
|
| - auth->username = cached_auth->username;
|
| - auth->password = cached_auth->password;
|
| - return RestartInternal();
|
| - }
|
| -
|
| - response_.auth_challenge.swap(auth_info);
|
| - return OK;
|
| -}
|
| -
|
| -static DWORD StringToAuthScheme(const std::wstring& scheme) {
|
| - if (LowerCaseEqualsASCII(scheme, "basic"))
|
| - return WINHTTP_AUTH_SCHEME_BASIC;
|
| - if (LowerCaseEqualsASCII(scheme, "digest"))
|
| - return WINHTTP_AUTH_SCHEME_DIGEST;
|
| - if (LowerCaseEqualsASCII(scheme, "ntlm"))
|
| - return WINHTTP_AUTH_SCHEME_NTLM;
|
| - if (LowerCaseEqualsASCII(scheme, "negotiate"))
|
| - return WINHTTP_AUTH_SCHEME_NEGOTIATE;
|
| - if (LowerCaseEqualsASCII(scheme, "passport1.4"))
|
| - return WINHTTP_AUTH_SCHEME_PASSPORT;
|
| - return 0;
|
| -}
|
| -
|
| -// Applies authentication credentials to request_handle_.
|
| -void HttpTransactionWinHttp::ApplyAuth() {
|
| - DWORD auth_scheme;
|
| - BOOL rv;
|
| - if (proxy_auth_ && proxy_auth_->state == AUTH_STATE_HAVE_AUTH) {
|
| - // Add auth data to cache.
|
| - DCHECK(!proxy_auth_cache_key_.empty());
|
| - session_->auth_cache()->Add(proxy_auth_cache_key_, proxy_auth_);
|
| - auth_scheme = StringToAuthScheme(proxy_auth_->scheme);
|
| - if (auth_scheme == 0)
|
| - return;
|
| -
|
| - rv = WinHttpSetCredentials(request_handle_,
|
| - WINHTTP_AUTH_TARGET_PROXY,
|
| - auth_scheme,
|
| - proxy_auth_->username.c_str(),
|
| - proxy_auth_->password.c_str(),
|
| - NULL);
|
| - }
|
| -
|
| - if (server_auth_ && server_auth_->state == AUTH_STATE_HAVE_AUTH) {
|
| - // Add auth data to cache.
|
| - DCHECK(!server_auth_cache_key_.empty());
|
| - session_->auth_cache()->Add(server_auth_cache_key_, server_auth_);
|
| - auth_scheme = StringToAuthScheme(server_auth_->scheme);
|
| - if (auth_scheme == 0)
|
| - return;
|
| -
|
| - rv = WinHttpSetCredentials(request_handle_,
|
| - WINHTTP_AUTH_TARGET_SERVER,
|
| - auth_scheme,
|
| - server_auth_->username.c_str(),
|
| - server_auth_->password.c_str(),
|
| - NULL);
|
| - }
|
| -}
|
| -
|
| -void HttpTransactionWinHttp::OnProxyInfoAvailable(int result) {
|
| - if (result != OK) {
|
| - DLOG(WARNING) << "failed to get proxy info: " << result;
|
| - proxy_info_.UseDirect();
|
| - }
|
| -
|
| - // Balances extra reference taken when proxy resolution was initiated.
|
| - session_callback_->Release();
|
| -
|
| - pac_request_ = NULL;
|
| -
|
| - // Since OnProxyInfoAvailable is always called asynchronously (via the
|
| - // message loop), we need to trap any errors and pass them to the consumer
|
| - // via their completion callback.
|
| -
|
| - int rv = DidResolveProxy();
|
| - if (rv == ERR_IO_PENDING) {
|
| - session_callback_->AddRef(); // balanced when callback runs.
|
| - } else {
|
| - DoCallback(rv);
|
| - }
|
| -}
|
| -
|
| -std::string HttpTransactionWinHttp::GetRequestHeaders() const {
|
| - std::string headers;
|
| -
|
| - if (!request_->user_agent.empty())
|
| - headers += "User-Agent: " + request_->user_agent + "\r\n";
|
| -
|
| - // Our consumer should have made sure that this is a safe referrer. See for
|
| - // instance WebCore::FrameLoader::HideReferrer.
|
| - if (request_->referrer.is_valid())
|
| - headers += "Referer: " + request_->referrer.spec() + "\r\n";
|
| -
|
| - // IE and Safari do this. Presumably it is to support sending a HEAD request
|
| - // to an URL that only expects to be sent a POST or some other method that
|
| - // normally would have a message body.
|
| - if (request_->method == "HEAD")
|
| - headers += "Content-Length: 0\r\n";
|
| -
|
| - // Honor load flags that impact proxy caches.
|
| - if (request_->load_flags & LOAD_BYPASS_CACHE) {
|
| - headers += "Pragma: no-cache\r\nCache-Control: no-cache\r\n";
|
| - } else if (request_->load_flags & LOAD_VALIDATE_CACHE) {
|
| - headers += "Cache-Control: max-age=0\r\n";
|
| - }
|
| -
|
| - // TODO(darin): Prune out duplicate headers?
|
| - headers += request_->extra_headers;
|
| -
|
| - return headers;
|
| -}
|
| -
|
| -// Retrieves the SSL server certificate associated with the transaction.
|
| -// The caller is responsible for freeing the certificate.
|
| -X509Certificate* HttpTransactionWinHttp::GetServerCertificate() const {
|
| - DCHECK(is_https_);
|
| - PCCERT_CONTEXT cert_context = NULL;
|
| - DWORD length = sizeof(cert_context);
|
| - if (!WinHttpQueryOption(request_handle_,
|
| - WINHTTP_OPTION_SERVER_CERT_CONTEXT,
|
| - &cert_context,
|
| - &length)) {
|
| - return NULL;
|
| - }
|
| - // cert_context may be NULL here even though WinHttpQueryOption succeeded.
|
| - // For example, a proxy server may return a 404 error page to report the
|
| - // DNS resolution failure of the server's hostname.
|
| - if (!cert_context)
|
| - return NULL;
|
| - return X509Certificate::CreateFromHandle(cert_context);
|
| -}
|
| -
|
| -// Retrieves the security strength, in bits, of the SSL cipher suite
|
| -// associated with the transaction.
|
| -int HttpTransactionWinHttp::GetSecurityBits() const {
|
| - DCHECK(is_https_);
|
| - DWORD key_bits = 0;
|
| - DWORD length = sizeof(key_bits);
|
| - if (!WinHttpQueryOption(request_handle_,
|
| - WINHTTP_OPTION_SECURITY_KEY_BITNESS,
|
| - &key_bits,
|
| - &length)) {
|
| - return -1;
|
| - }
|
| - return key_bits;
|
| -}
|
| -
|
| -void HttpTransactionWinHttp::PopulateSSLInfo(DWORD secure_failure) {
|
| - if (is_https_) {
|
| - response_.ssl_info.cert = GetServerCertificate();
|
| - response_.ssl_info.security_bits = GetSecurityBits();
|
| - // If there is no cert (such as when the proxy server makes up a
|
| - // 404 response to report a server name resolution error), don't set
|
| - // the cert status.
|
| - if (!response_.ssl_info.cert)
|
| - return;
|
| - response_.ssl_info.cert_status =
|
| - MapSecureFailureToCertStatus(secure_failure);
|
| - // WinHTTP does not always return a cert status once we ignored errors
|
| - // for a cert. (Our experiments showed that WinHTTP reliably returns a
|
| - // cert status only when there are unignored errors or when we resend a
|
| - // request with the errors ignored.) So we have to remember what the
|
| - // last status was for a cert. Note that if the cert status changes
|
| - // from error to OK, we won't know that. If we have never stored our
|
| - // status in the CertStatusCache (meaning no errors so far), then it is
|
| - // OK (0).
|
| - CertStatusCache* cert_status_cache = session_->cert_status_cache();
|
| - if (net::IsCertStatusError(response_.ssl_info.cert_status)) {
|
| - cert_status_cache->SetCertStatus(*response_.ssl_info.cert,
|
| - request_->url.host(),
|
| - response_.ssl_info.cert_status);
|
| - } else {
|
| - response_.ssl_info.cert_status |=
|
| - cert_status_cache->GetCertStatus(*response_.ssl_info.cert,
|
| - request_->url.host()) &
|
| - net::CERT_STATUS_ALL_ERRORS;
|
| - }
|
| -
|
| - if (rev_checking_enabled_)
|
| - response_.ssl_info.cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
|
| - } else {
|
| - // If this is not https, we should not get a cert status.
|
| - DCHECK(!secure_failure);
|
| - }
|
| -}
|
| -
|
| -void HttpTransactionWinHttp::HandleStatusCallback(DWORD status,
|
| - DWORD_PTR result,
|
| - DWORD error,
|
| - DWORD secure_failure) {
|
| - int rv;
|
| -
|
| - switch (status) {
|
| - case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
|
| - rv = DidReceiveError(error, secure_failure);
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
|
| - PopulateSSLInfo(secure_failure);
|
| - rv = DidSendRequest();
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
|
| - rv = DidWriteData(static_cast<DWORD>(result));
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
|
| - rv = DidReceiveHeaders();
|
| - break;
|
| - case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
|
| - rv = DidReadData(static_cast<DWORD>(result));
|
| - break;
|
| - default:
|
| - NOTREACHED() << "unexpected status code";
|
| - rv = ERR_UNEXPECTED;
|
| - break;
|
| - }
|
| -
|
| - if (rv == ERR_IO_PENDING) {
|
| - session_callback_->AddRef(); // balanced when callback runs.
|
| - } else if (callback_) {
|
| - DoCallback(rv);
|
| - }
|
| -}
|
| -
|
| -} // namespace net
|
| -
|
|
|