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 |
- |