OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/http/http_transaction_winhttp.h" | |
6 | |
7 #include <winhttp.h> | |
8 | |
9 #include "base/lock.h" | |
10 #include "base/memory_debug.h" | |
11 #include "base/message_loop.h" | |
12 #include "base/string_piece.h" | |
13 #include "base/string_util.h" | |
14 #include "base/sys_string_conversions.h" | |
15 #include "googleurl/src/gurl.h" | |
16 #include "net/base/auth_cache.h" | |
17 #include "net/base/cert_status_flags.h" | |
18 #include "net/base/dns_resolution_observer.h" | |
19 #include "net/base/load_flags.h" | |
20 #include "net/base/net_errors.h" | |
21 #include "net/base/net_util.h" | |
22 #include "net/base/ssl_config_service.h" | |
23 #include "net/base/upload_data_stream.h" | |
24 #include "net/http/cert_status_cache.h" | |
25 #include "net/http/http_request_info.h" | |
26 #include "net/http/winhttp_request_throttle.h" | |
27 #include "net/proxy/proxy_resolver_winhttp.h" | |
28 | |
29 #pragma comment(lib, "winhttp.lib") | |
30 #pragma warning(disable: 4355) | |
31 | |
32 using base::Time; | |
33 | |
34 namespace net { | |
35 | |
36 static int TranslateOSError(DWORD error) { | |
37 switch (error) { | |
38 case ERROR_SUCCESS: | |
39 return OK; | |
40 case ERROR_FILE_NOT_FOUND: | |
41 return ERR_FILE_NOT_FOUND; | |
42 case ERROR_HANDLE_EOF: // TODO(wtc): return OK? | |
43 return ERR_CONNECTION_CLOSED; | |
44 case ERROR_INVALID_HANDLE: | |
45 return ERR_INVALID_HANDLE; | |
46 case ERROR_INVALID_PARAMETER: | |
47 return ERR_INVALID_ARGUMENT; | |
48 | |
49 case ERROR_WINHTTP_CANNOT_CONNECT: | |
50 return ERR_CONNECTION_FAILED; | |
51 case ERROR_WINHTTP_TIMEOUT: | |
52 return ERR_TIMED_OUT; | |
53 case ERROR_WINHTTP_INVALID_URL: | |
54 return ERR_INVALID_URL; | |
55 case ERROR_WINHTTP_NAME_NOT_RESOLVED: | |
56 return ERR_NAME_NOT_RESOLVED; | |
57 case ERROR_WINHTTP_OPERATION_CANCELLED: | |
58 return ERR_ABORTED; | |
59 case ERROR_WINHTTP_SECURE_CHANNEL_ERROR: | |
60 case ERROR_WINHTTP_SECURE_FAILURE: | |
61 case SEC_E_ILLEGAL_MESSAGE: | |
62 return ERR_SSL_PROTOCOL_ERROR; | |
63 case SEC_E_ALGORITHM_MISMATCH: | |
64 return ERR_SSL_VERSION_OR_CIPHER_MISMATCH; | |
65 case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED: | |
66 return ERR_SSL_CLIENT_AUTH_CERT_NEEDED; | |
67 case ERROR_WINHTTP_UNRECOGNIZED_SCHEME: | |
68 return ERR_UNKNOWN_URL_SCHEME; | |
69 case ERROR_WINHTTP_INVALID_SERVER_RESPONSE: | |
70 return ERR_INVALID_RESPONSE; | |
71 | |
72 // SSL certificate errors | |
73 case ERROR_WINHTTP_SECURE_CERT_CN_INVALID: | |
74 return ERR_CERT_COMMON_NAME_INVALID; | |
75 case ERROR_WINHTTP_SECURE_CERT_DATE_INVALID: | |
76 return ERR_CERT_DATE_INVALID; | |
77 case ERROR_WINHTTP_SECURE_INVALID_CA: | |
78 return ERR_CERT_AUTHORITY_INVALID; | |
79 case ERROR_WINHTTP_SECURE_CERT_REV_FAILED: | |
80 return ERR_CERT_UNABLE_TO_CHECK_REVOCATION; | |
81 case ERROR_WINHTTP_SECURE_CERT_REVOKED: | |
82 return ERR_CERT_REVOKED; | |
83 case ERROR_WINHTTP_SECURE_INVALID_CERT: | |
84 return ERR_CERT_INVALID; | |
85 | |
86 default: | |
87 DCHECK(error != ERROR_IO_PENDING); // WinHTTP doesn't use this error. | |
88 LOG(WARNING) << "Unknown error " << error | |
89 << " mapped to net::ERR_FAILED"; | |
90 return ERR_FAILED; | |
91 } | |
92 } | |
93 | |
94 static int TranslateLastOSError() { | |
95 return TranslateOSError(GetLastError()); | |
96 } | |
97 | |
98 // Clear certificate errors that we want to ignore. | |
99 static DWORD FilterSecureFailure(DWORD status, int load_flags) { | |
100 if (load_flags & LOAD_IGNORE_CERT_COMMON_NAME_INVALID) | |
101 status &= ~WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID; | |
102 if (load_flags & LOAD_IGNORE_CERT_DATE_INVALID) | |
103 status &= ~WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID; | |
104 if (load_flags & LOAD_IGNORE_CERT_AUTHORITY_INVALID) | |
105 status &= ~WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA; | |
106 if (load_flags & LOAD_IGNORE_CERT_WRONG_USAGE) | |
107 status &= ~WINHTTP_CALLBACK_STATUS_FLAG_CERT_WRONG_USAGE; | |
108 return status; | |
109 } | |
110 | |
111 static DWORD MapSecureFailureToError(DWORD status) { | |
112 // A certificate may have multiple errors. We report the most | |
113 // serious error. | |
114 | |
115 // Unrecoverable errors | |
116 if (status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR) | |
117 return ERROR_WINHTTP_SECURE_CHANNEL_ERROR; | |
118 if (status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT) | |
119 return ERROR_WINHTTP_SECURE_INVALID_CERT; | |
120 if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED) | |
121 return ERROR_WINHTTP_SECURE_CERT_REVOKED; | |
122 | |
123 // Recoverable errors | |
124 if (status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA) | |
125 return ERROR_WINHTTP_SECURE_INVALID_CA; | |
126 if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID) | |
127 return ERROR_WINHTTP_SECURE_CERT_CN_INVALID; | |
128 if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID) | |
129 return ERROR_WINHTTP_SECURE_CERT_DATE_INVALID; | |
130 if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_WRONG_USAGE) | |
131 return ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE; | |
132 | |
133 // Unknown status. Give it the benefit of the doubt. | |
134 if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED) | |
135 return ERROR_WINHTTP_SECURE_CERT_REV_FAILED; | |
136 | |
137 // Map a status of 0 to the generic secure failure error. We have seen a | |
138 // case where WinHttp doesn't notify us of a secure failure (so status is 0) | |
139 // before notifying us of a request error with ERROR_WINHTTP_SECURE_FAILURE. | |
140 // (WinInet fails with ERROR_INTERNET_SECURITY_CHANNEL_ERROR in that case.) | |
141 return ERROR_WINHTTP_SECURE_FAILURE; | |
142 } | |
143 | |
144 static int MapSecureFailureToCertStatus(DWORD status) { | |
145 int cert_status = 0; | |
146 | |
147 if (status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT) | |
148 cert_status |= CERT_STATUS_INVALID; | |
149 if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED) | |
150 cert_status |= CERT_STATUS_REVOKED; | |
151 if (status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA) | |
152 cert_status |= CERT_STATUS_AUTHORITY_INVALID; | |
153 if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID) | |
154 cert_status |= CERT_STATUS_COMMON_NAME_INVALID; | |
155 if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID) | |
156 cert_status |= CERT_STATUS_DATE_INVALID; | |
157 if (status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED) | |
158 cert_status |= CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; | |
159 | |
160 return cert_status; | |
161 // TODO(jcampan): what about ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE? | |
162 } | |
163 | |
164 // Session -------------------------------------------------------------------- | |
165 | |
166 class HttpTransactionWinHttp::Session | |
167 : public base::RefCounted<HttpTransactionWinHttp::Session> { | |
168 public: | |
169 enum { | |
170 // By default WinHTTP enables only SSL3 and TLS1. | |
171 SECURE_PROTOCOLS_SSL3_TLS1 = WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | | |
172 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | |
173 }; | |
174 | |
175 explicit Session(ProxyService* proxy_service); | |
176 | |
177 // Opens the primary WinHttp session handle. | |
178 bool Init(const std::string& user_agent); | |
179 | |
180 // Opens the alternative WinHttp session handle for TLS-intolerant servers. | |
181 bool InitNoTLS(const std::string& user_agent); | |
182 | |
183 void AddRefBySessionCallback(); | |
184 | |
185 void ReleaseBySessionCallback(); | |
186 | |
187 // The primary WinHttp session handle. | |
188 HINTERNET internet() { return internet_; } | |
189 | |
190 // An alternative WinHttp session handle. It is not opened until we have | |
191 // encountered a TLS-intolerant server and used for those servers only. | |
192 // TLS is disabled in this session. | |
193 HINTERNET internet_no_tls() { return internet_no_tls_; } | |
194 | |
195 // The message loop of the thread where the session was created. | |
196 MessageLoop* message_loop() { return message_loop_; } | |
197 | |
198 ProxyService* proxy_service() { return proxy_service_; } | |
199 | |
200 // Gets the HTTP authentication cache for the session. | |
201 AuthCache* auth_cache() { return &auth_cache_; } | |
202 | |
203 HANDLE handle_closing_event() const { return handle_closing_event_; } | |
204 | |
205 CertStatusCache* cert_status_cache() { return &cert_status_cache_; } | |
206 | |
207 bool rev_checking_enabled() const { return rev_checking_enabled_; } | |
208 | |
209 bool tls_enabled() const { | |
210 return (secure_protocols_ & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) != 0; | |
211 } | |
212 | |
213 bool ShouldIgnoreCertRev(const std::string& origin) const { | |
214 OriginSet::const_iterator pos = ignore_cert_rev_servers_.find(origin); | |
215 return pos != ignore_cert_rev_servers_.end(); | |
216 } | |
217 | |
218 void IgnoreCertRev(const std::string& origin) { | |
219 ignore_cert_rev_servers_.insert(origin); | |
220 } | |
221 | |
222 WinHttpRequestThrottle* request_throttle() { | |
223 return &request_throttle_; | |
224 } | |
225 | |
226 private: | |
227 friend class base::RefCounted<HttpTransactionWinHttp::Session>; | |
228 | |
229 ~Session(); | |
230 | |
231 // Called by the destructor only. | |
232 void WaitUntilCallbacksAllDone(); | |
233 | |
234 HINTERNET OpenWinHttpSession(const std::string& user_agent); | |
235 | |
236 // Get the SSL configuration settings and save them in rev_checking_enabled_ | |
237 // and secure_protocols_. | |
238 void GetSSLConfig(); | |
239 | |
240 HINTERNET internet_; | |
241 HINTERNET internet_no_tls_; | |
242 MessageLoop* message_loop_; | |
243 ProxyService* proxy_service_; | |
244 AuthCache auth_cache_; | |
245 | |
246 // This event object is used when destroying a transaction. It is given | |
247 // to the transaction's session callback if WinHTTP still has the caller's | |
248 // data (request info or read buffer) and we need to wait until WinHTTP is | |
249 // done with the data. | |
250 HANDLE handle_closing_event_; | |
251 | |
252 // The following members ensure a clean destruction of the Session object. | |
253 // The Session destructor waits until all the request handles have been | |
254 // terminated by WinHTTP, at which point no more status callbacks will | |
255 // reference the MessageLoop of the Session. | |
256 // | |
257 // quit_event_ is the event object used for this wait. | |
258 // | |
259 // lock_ protects session_callback_ref_count_ and quitting_. | |
260 // | |
261 // session_callback_ref_count_ is the number of SessionCallback objects | |
262 // that may reference the MessageLoop of the Session. | |
263 // | |
264 // The boolean quitting_ is true when the Session object is being | |
265 // destructed. | |
266 HANDLE quit_event_; | |
267 Lock lock_; | |
268 int session_callback_ref_count_; | |
269 bool quitting_; | |
270 | |
271 // We use a cache to store the certificate error as we cannot always rely on | |
272 // WinHTTP to provide us the SSL error once we restarted a connection asking | |
273 // to ignored errors. | |
274 CertStatusCache cert_status_cache_; | |
275 | |
276 // SSL settings | |
277 bool rev_checking_enabled_; | |
278 DWORD secure_protocols_; | |
279 | |
280 // The servers for which certificate revocation should be ignored. | |
281 // | |
282 // WinHTTP verifies each certificate only once and caches the certificate | |
283 // verification results, so if we ever ignore certificate revocation for a | |
284 // server, we cannot enable revocation checking again for that server for | |
285 // the rest of the session. | |
286 // | |
287 // If we honor changes to the rev_checking_enabled system setting during | |
288 // the session, we will have to remember all the servers we have visited | |
289 // while the rev_checking_enabled setting is false. This will consume a | |
290 // lot of memory. So we now require the users to restart Chrome for a | |
291 // rev_checking_enabled change to take effect, just like IE does. | |
292 typedef std::set<std::string> OriginSet; | |
293 OriginSet ignore_cert_rev_servers_; | |
294 | |
295 WinHttpRequestThrottle request_throttle_; | |
296 }; | |
297 | |
298 HttpTransactionWinHttp::Session::Session(ProxyService* proxy_service) | |
299 : internet_(NULL), | |
300 internet_no_tls_(NULL), | |
301 proxy_service_(proxy_service), | |
302 session_callback_ref_count_(0), | |
303 quitting_(false) { | |
304 GetSSLConfig(); | |
305 | |
306 // Save the current message loop for callback notifications. | |
307 message_loop_ = MessageLoop::current(); | |
308 | |
309 handle_closing_event_ = CreateEvent(NULL, | |
310 FALSE, // auto-reset | |
311 FALSE, // initially nonsignaled | |
312 NULL); // unnamed | |
313 | |
314 quit_event_ = CreateEvent(NULL, | |
315 FALSE, // auto-reset | |
316 FALSE, // initially nonsignaled | |
317 NULL); // unnamed | |
318 } | |
319 | |
320 HttpTransactionWinHttp::Session::~Session() { | |
321 if (internet_) { | |
322 WinHttpCloseHandle(internet_); | |
323 if (internet_no_tls_) | |
324 WinHttpCloseHandle(internet_no_tls_); | |
325 | |
326 // Ensure that all status callbacks that may reference the MessageLoop | |
327 // of this thread are done before we can allow the current thread to exit. | |
328 WaitUntilCallbacksAllDone(); | |
329 } | |
330 | |
331 if (handle_closing_event_) | |
332 CloseHandle(handle_closing_event_); | |
333 if (quit_event_) | |
334 CloseHandle(quit_event_); | |
335 } | |
336 | |
337 bool HttpTransactionWinHttp::Session::Init(const std::string& user_agent) { | |
338 DCHECK(!internet_); | |
339 | |
340 internet_ = OpenWinHttpSession(user_agent); | |
341 | |
342 if (!internet_) | |
343 return false; | |
344 | |
345 if (secure_protocols_ != SECURE_PROTOCOLS_SSL3_TLS1) { | |
346 BOOL rv = WinHttpSetOption(internet_, WINHTTP_OPTION_SECURE_PROTOCOLS, | |
347 &secure_protocols_, sizeof(secure_protocols_)); | |
348 DCHECK(rv); | |
349 } | |
350 | |
351 return true; | |
352 } | |
353 | |
354 bool HttpTransactionWinHttp::Session::InitNoTLS( | |
355 const std::string& user_agent) { | |
356 DCHECK(tls_enabled()); | |
357 DCHECK(internet_); | |
358 DCHECK(!internet_no_tls_); | |
359 | |
360 internet_no_tls_ = OpenWinHttpSession(user_agent); | |
361 | |
362 if (!internet_no_tls_) | |
363 return false; | |
364 | |
365 DWORD protocols = secure_protocols_ & ~WINHTTP_FLAG_SECURE_PROTOCOL_TLS1; | |
366 BOOL rv = WinHttpSetOption(internet_no_tls_, | |
367 WINHTTP_OPTION_SECURE_PROTOCOLS, | |
368 &protocols, sizeof(protocols)); | |
369 DCHECK(rv); | |
370 | |
371 return true; | |
372 } | |
373 | |
374 void HttpTransactionWinHttp::Session::AddRefBySessionCallback() { | |
375 AutoLock lock(lock_); | |
376 session_callback_ref_count_++; | |
377 } | |
378 | |
379 void HttpTransactionWinHttp::Session::ReleaseBySessionCallback() { | |
380 bool need_to_signal; | |
381 { | |
382 AutoLock lock(lock_); | |
383 session_callback_ref_count_--; | |
384 need_to_signal = (quitting_ && session_callback_ref_count_ == 0); | |
385 } | |
386 if (need_to_signal) | |
387 SetEvent(quit_event_); | |
388 } | |
389 | |
390 // This is called by the Session destructor only. By now the transaction | |
391 // factory and all the transactions have been destructed. This means that | |
392 // new transactions can't be created, and existing transactions can't be | |
393 // started, which in turn implies that session_callback_ref_count_ cannot | |
394 // increase. We wait until session_callback_ref_count_ drops to 0. | |
395 void HttpTransactionWinHttp::Session::WaitUntilCallbacksAllDone() { | |
396 bool need_to_wait; | |
397 { | |
398 AutoLock lock(lock_); | |
399 quitting_ = true; | |
400 need_to_wait = (session_callback_ref_count_ != 0); | |
401 } | |
402 if (need_to_wait) | |
403 WaitForSingleObject(quit_event_, INFINITE); | |
404 DCHECK(session_callback_ref_count_ == 0); | |
405 } | |
406 | |
407 HINTERNET HttpTransactionWinHttp::Session::OpenWinHttpSession( | |
408 const std::string& user_agent) { | |
409 // Proxy config will be set explicitly for each request. | |
410 // | |
411 // Although UA string will also be set explicitly for each request, HTTP | |
412 // CONNECT requests use the UA string of the session handle, so we have to | |
413 // pass a UA string to WinHttpOpen. | |
414 HINTERNET internet = WinHttpOpen(ASCIIToWide(user_agent).c_str(), | |
415 WINHTTP_ACCESS_TYPE_NO_PROXY, | |
416 WINHTTP_NO_PROXY_NAME, | |
417 WINHTTP_NO_PROXY_BYPASS, | |
418 WINHTTP_FLAG_ASYNC); | |
419 if (!internet) | |
420 return internet; | |
421 | |
422 // Use a 90-second timeout (1.5 times the default) for connect. Disable | |
423 // name resolution, send, and receive timeouts. We expect our consumer to | |
424 // apply timeouts or provide controls for users to stop requests that are | |
425 // taking too long. | |
426 BOOL rv = WinHttpSetTimeouts(internet, 0, 90000, 0, 0); | |
427 DCHECK(rv); | |
428 | |
429 return internet; | |
430 } | |
431 | |
432 void HttpTransactionWinHttp::Session::GetSSLConfig() { | |
433 SSLConfig ssl_config; | |
434 SSLConfigService::GetSSLConfigNow(&ssl_config); | |
435 rev_checking_enabled_ = ssl_config.rev_checking_enabled; | |
436 secure_protocols_ = 0; | |
437 if (ssl_config.ssl2_enabled) | |
438 secure_protocols_ |= WINHTTP_FLAG_SECURE_PROTOCOL_SSL2; | |
439 if (ssl_config.ssl3_enabled) | |
440 secure_protocols_ |= WINHTTP_FLAG_SECURE_PROTOCOL_SSL3; | |
441 if (ssl_config.tls1_enabled) | |
442 secure_protocols_ |= WINHTTP_FLAG_SECURE_PROTOCOL_TLS1; | |
443 } | |
444 | |
445 // SessionCallback ------------------------------------------------------------ | |
446 | |
447 class HttpTransactionWinHttp::SessionCallback | |
448 : public base::RefCountedThreadSafe<HttpTransactionWinHttp::SessionCallback> { | |
449 public: | |
450 SessionCallback(HttpTransactionWinHttp* trans, Session* session) | |
451 : trans_(trans), | |
452 session_(session), | |
453 load_state_(LOAD_STATE_IDLE), | |
454 handle_closing_event_(NULL), | |
455 bytes_available_(0), | |
456 read_buf_(NULL), | |
457 read_buf_len_(0), | |
458 secure_failure_(0), | |
459 connection_was_opened_(false), | |
460 request_was_probably_sent_(false), | |
461 response_was_received_(false), | |
462 response_is_empty_(true) { | |
463 } | |
464 | |
465 // Called when the associated trans_ has to reopen its connection and | |
466 // request handles to recover from certain SSL errors. Resets the members | |
467 // that may have been modified at that point. | |
468 void ResetForNewRequest() { | |
469 secure_failure_ = 0; | |
470 connection_was_opened_ = false; | |
471 } | |
472 | |
473 void DropTransaction() { | |
474 trans_ = NULL; | |
475 } | |
476 | |
477 void Notify(DWORD status, DWORD_PTR result, DWORD error) { | |
478 DWORD secure_failure = 0; | |
479 if (status == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR) { | |
480 switch (error) { | |
481 // WinHttp sends this error code in two interesting cases: 1) when a | |
482 // response header is malformed, and 2) when a response is empty. In | |
483 // the latter case, we want to actually resend the request if the | |
484 // request was sent over a reused "keep-alive" connection. This is a | |
485 // risky thing to do since it is possible that the server did receive | |
486 // our request, but it is unfortunately required to support HTTP keep- | |
487 // alive connections properly, and other browsers all do this too. | |
488 case ERROR_WINHTTP_INVALID_SERVER_RESPONSE: | |
489 if (empty_response_was_received() && !connection_was_opened_) | |
490 error = ERROR_WINHTTP_RESEND_REQUEST; | |
491 break; | |
492 case ERROR_WINHTTP_SECURE_FAILURE: | |
493 secure_failure = secure_failure_; | |
494 break; | |
495 } | |
496 } else if (status == WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE) { | |
497 secure_failure = secure_failure_; | |
498 } | |
499 session_->message_loop()->PostTask(FROM_HERE, | |
500 NewRunnableMethod(this, | |
501 &HttpTransactionWinHttp::SessionCallback::OnNotify, | |
502 status, result, error, secure_failure)); | |
503 } | |
504 | |
505 // Calls WinHttpReadData and returns its return value. | |
506 BOOL ReadData(HINTERNET request_handle); | |
507 | |
508 void OnHandleClosing() { | |
509 if (handle_closing_event_) | |
510 SetEvent(handle_closing_event_); | |
511 session_->ReleaseBySessionCallback(); | |
512 Release(); | |
513 } | |
514 | |
515 // Modified from any thread. | |
516 void set_load_state(LoadState state) { | |
517 load_state_ = state; | |
518 } | |
519 LoadState load_state() const { | |
520 return load_state_; | |
521 } | |
522 | |
523 int bytes_available() const { return bytes_available_; } | |
524 void set_bytes_available(int n) { bytes_available_ = n; } | |
525 void reduce_bytes_available(int n) { bytes_available_ -= n; } | |
526 | |
527 char* read_buf() const { return read_buf_; } | |
528 void set_read_buf(char* buf) { read_buf_ = buf; } | |
529 | |
530 int read_buf_len() const { return read_buf_len_; } | |
531 void set_read_buf_len(int buf_len) { read_buf_len_ = buf_len; } | |
532 | |
533 // Tells this SessionCallback to signal this event when receiving the | |
534 // handle closing status callback. | |
535 void set_handle_closing_event(HANDLE event) { | |
536 handle_closing_event_ = event; | |
537 } | |
538 | |
539 void set_secure_failure(DWORD flags) { secure_failure_ = flags; } | |
540 | |
541 void did_open_connection() { | |
542 connection_was_opened_ = true; | |
543 } | |
544 | |
545 void did_start_sending_request() { | |
546 request_was_probably_sent_ = true; | |
547 } | |
548 bool request_was_probably_sent() const { | |
549 return request_was_probably_sent_; | |
550 } | |
551 | |
552 void did_receive_bytes(DWORD count) { | |
553 response_was_received_ = true; | |
554 if (count) | |
555 response_is_empty_ = false; | |
556 } | |
557 | |
558 private: | |
559 friend base::RefCountedThreadSafe<HttpTransactionWinHttp::SessionCallback>; | |
560 ~SessionCallback() {} | |
561 | |
562 void OnNotify(DWORD status, | |
563 DWORD_PTR result, | |
564 DWORD error, | |
565 DWORD secure_failure) { | |
566 if (trans_) | |
567 trans_->HandleStatusCallback(status, result, error, secure_failure); | |
568 | |
569 // Balances the AddRefs made by the transaction object after an async | |
570 // WinHTTP call. | |
571 Release(); | |
572 } | |
573 | |
574 bool empty_response_was_received() const { | |
575 return response_was_received_ && response_is_empty_; | |
576 } | |
577 | |
578 HttpTransactionWinHttp* trans_; | |
579 | |
580 // Session is reference-counted, but this is a plain pointer. The | |
581 // reference on the Session owned by SessionCallback is managed using | |
582 // Session::AddRefBySessionCallback and Session::ReleaseBySessionCallback. | |
583 Session* session_; | |
584 | |
585 // Modified from any thread. | |
586 volatile LoadState load_state_; | |
587 | |
588 // Amount of data available reported by WinHttpQueryDataAvailable that | |
589 // haven't been consumed by WinHttpReadData. | |
590 int bytes_available_; | |
591 | |
592 // Caller's read buffer and buffer size, to be passed to WinHttpReadData. | |
593 // These are used by the IO thread and the thread WinHTTP uses to make | |
594 // status callbacks, but not at the same time. | |
595 char* read_buf_; | |
596 int read_buf_len_; | |
597 | |
598 // If not null, we set this event on receiving the handle closing callback. | |
599 HANDLE handle_closing_event_; | |
600 | |
601 // The secure connection failure flags reported by the | |
602 // WINHTTP_CALLBACK_STATUS_SECURE_FAILURE status callback. | |
603 DWORD secure_failure_; | |
604 | |
605 // True if a connection was opened for this request. | |
606 bool connection_was_opened_; | |
607 | |
608 // True if the request may have been sent to the server (and therefore we | |
609 // should not restart the request). | |
610 bool request_was_probably_sent_; | |
611 | |
612 // True if any response was received. | |
613 bool response_was_received_; | |
614 | |
615 // True if we have an empty response (no headers, no status line, nothing). | |
616 bool response_is_empty_; | |
617 }; | |
618 | |
619 BOOL HttpTransactionWinHttp::SessionCallback::ReadData( | |
620 HINTERNET request_handle) { | |
621 DCHECK(bytes_available_ >= 0); | |
622 char* buf = read_buf_; | |
623 read_buf_ = NULL; | |
624 int bytes_to_read = std::min(bytes_available_, read_buf_len_); | |
625 read_buf_len_ = 0; | |
626 if (!bytes_to_read) | |
627 bytes_to_read = 1; | |
628 | |
629 // Because of how WinHTTP fills memory when used asynchronously, Purify isn't | |
630 // able to detect that it's been initialized, so it scans for 0xcd in the | |
631 // buffer and reports UMRs (uninitialized memory reads) for those individual | |
632 // bytes. We override that to avoid the false error reports. | |
633 // See http://b/issue?id=1173916. | |
634 base::MemoryDebug::MarkAsInitialized(buf, bytes_to_read); | |
635 return WinHttpReadData(request_handle, buf, bytes_to_read, NULL); | |
636 } | |
637 | |
638 // static | |
639 void HttpTransactionWinHttp::StatusCallback(HINTERNET handle, | |
640 DWORD_PTR context, | |
641 DWORD status, | |
642 LPVOID status_info, | |
643 DWORD status_info_len) { | |
644 SessionCallback* callback = reinterpret_cast<SessionCallback*>(context); | |
645 | |
646 switch (status) { | |
647 case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING: | |
648 if (callback) | |
649 callback->OnHandleClosing(); | |
650 break; | |
651 case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER: | |
652 callback->set_load_state(LOAD_STATE_CONNECTING); | |
653 break; | |
654 case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER: | |
655 callback->did_open_connection(); | |
656 break; | |
657 case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: | |
658 callback->set_load_state(LOAD_STATE_SENDING_REQUEST); | |
659 callback->did_start_sending_request(); | |
660 break; | |
661 case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE: | |
662 callback->set_load_state(LOAD_STATE_WAITING_FOR_RESPONSE); | |
663 break; | |
664 case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED: | |
665 callback->did_receive_bytes(*static_cast<DWORD*>(status_info)); | |
666 break; | |
667 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: | |
668 DCHECK(callback->bytes_available() == 0); | |
669 DCHECK(status_info_len == sizeof(DWORD)); | |
670 callback->set_bytes_available(static_cast<DWORD*>(status_info)[0]); | |
671 if (!callback->ReadData(handle)) | |
672 callback->Notify(WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, | |
673 API_READ_DATA, GetLastError()); | |
674 break; | |
675 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: | |
676 callback->Notify(status, status_info_len, 0); | |
677 break; | |
678 case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: | |
679 DCHECK(status_info_len == sizeof(DWORD)); | |
680 callback->Notify(status, static_cast<DWORD*>(status_info)[0], 0); | |
681 break; | |
682 case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: | |
683 callback->Notify(status, TRUE, 0); | |
684 break; | |
685 case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: | |
686 callback->Notify(status, TRUE, 0); | |
687 break; | |
688 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: { | |
689 WINHTTP_ASYNC_RESULT* result = | |
690 static_cast<WINHTTP_ASYNC_RESULT*>(status_info); | |
691 callback->Notify(status, result->dwResult, result->dwError); | |
692 if (API_SEND_REQUEST == result->dwResult && | |
693 ERROR_WINHTTP_NAME_NOT_RESOLVED == result->dwError) | |
694 DidFinishDnsResolutionWithStatus(false, | |
695 GURL(), // null referrer URL. | |
696 reinterpret_cast<void*>(context)); | |
697 break; | |
698 } | |
699 // This status callback provides the detailed reason for a secure | |
700 // failure. We map that to an error code and save it for later use. | |
701 case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: { | |
702 DCHECK(status_info_len == sizeof(DWORD)); | |
703 DWORD* status_ptr = static_cast<DWORD*>(status_info); | |
704 callback->set_secure_failure(*status_ptr); | |
705 break; | |
706 } | |
707 // Looking up the IP address of a server name. The status_info | |
708 // parameter contains a pointer to the server name being resolved. | |
709 case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME: { | |
710 callback->set_load_state(LOAD_STATE_RESOLVING_HOST); | |
711 std::wstring wname(static_cast<wchar_t*>(status_info), | |
712 status_info_len - 1); | |
713 DidStartDnsResolution(WideToASCII(wname), | |
714 reinterpret_cast<void*>(context)); | |
715 break; | |
716 } | |
717 // Successfully found the IP address of the server. | |
718 case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED: | |
719 DidFinishDnsResolutionWithStatus(true, | |
720 GURL(), // null referrer URL. | |
721 reinterpret_cast<void*>(context)); | |
722 break; | |
723 } | |
724 } | |
725 | |
726 // Factory -------------------------------------------------------------------- | |
727 | |
728 HttpTransactionWinHttp::Factory::~Factory() { | |
729 if (session_) | |
730 session_->Release(); | |
731 } | |
732 | |
733 HttpTransaction* HttpTransactionWinHttp::Factory::CreateTransaction() { | |
734 if (is_suspended_) | |
735 return NULL; | |
736 | |
737 if (!session_) { | |
738 session_ = new Session(proxy_service_); | |
739 session_->AddRef(); | |
740 } | |
741 return new HttpTransactionWinHttp(session_, proxy_service_->proxy_info()); | |
742 } | |
743 | |
744 HttpCache* HttpTransactionWinHttp::Factory::GetCache() { | |
745 return NULL; | |
746 } | |
747 | |
748 void HttpTransactionWinHttp::Factory::Suspend(bool suspend) { | |
749 is_suspended_ = suspend; | |
750 | |
751 if (is_suspended_ && session_) { | |
752 session_->Release(); | |
753 session_ = NULL; | |
754 } | |
755 } | |
756 | |
757 // Transaction ---------------------------------------------------------------- | |
758 | |
759 HttpTransactionWinHttp::HttpTransactionWinHttp(Session* session, | |
760 const ProxyInfo* info) | |
761 : session_(session), | |
762 request_(NULL), | |
763 load_flags_(0), | |
764 last_error_(ERROR_SUCCESS), | |
765 content_length_remaining_(-1), | |
766 pac_request_(NULL), | |
767 proxy_callback_(this, &HttpTransactionWinHttp::OnProxyInfoAvailable), | |
768 callback_(NULL), | |
769 upload_progress_(0), | |
770 connect_handle_(NULL), | |
771 request_handle_(NULL), | |
772 is_https_(false), | |
773 is_tls_intolerant_(false), | |
774 rev_checking_enabled_(false), | |
775 have_proxy_info_(false), | |
776 need_to_wait_for_handle_closing_(false), | |
777 request_submitted_(false), | |
778 used_embedded_credentials_(false) { | |
779 session->AddRef(); | |
780 session_callback_ = new SessionCallback(this, session); | |
781 if (info) { | |
782 proxy_info_.Use(*info); | |
783 have_proxy_info_ = true; | |
784 } | |
785 } | |
786 | |
787 HttpTransactionWinHttp::~HttpTransactionWinHttp() { | |
788 if (pac_request_) | |
789 session_->proxy_service()->CancelPacRequest(pac_request_); | |
790 | |
791 if (request_handle_) { | |
792 if (need_to_wait_for_handle_closing_) { | |
793 session_callback_->set_handle_closing_event( | |
794 session_->handle_closing_event()); | |
795 } | |
796 WinHttpCloseHandle(request_handle_); | |
797 if (need_to_wait_for_handle_closing_) | |
798 WaitForSingleObject(session_->handle_closing_event(), INFINITE); | |
799 } | |
800 if (connect_handle_) | |
801 WinHttpCloseHandle(connect_handle_); | |
802 | |
803 if (request_submitted_) { | |
804 session_->request_throttle()->RemoveRequest(connect_peer_, | |
805 request_handle_); | |
806 } | |
807 | |
808 if (session_callback_) { | |
809 session_callback_->DropTransaction(); | |
810 session_callback_ = NULL; // Release() reference as side effect. | |
811 } | |
812 if (session_) | |
813 session_->Release(); | |
814 } | |
815 | |
816 int HttpTransactionWinHttp::Start(const HttpRequestInfo* request_info, | |
817 CompletionCallback* callback) { | |
818 DCHECK(request_info); | |
819 DCHECK(callback); | |
820 | |
821 // ensure that we only have one asynchronous call at a time. | |
822 DCHECK(!callback_); | |
823 | |
824 LOG(INFO) << request_info->method << ": " << request_info->url; | |
825 | |
826 request_ = request_info; | |
827 load_flags_ = request_info->load_flags; | |
828 | |
829 int rv = OK; | |
830 if (!have_proxy_info_) { | |
831 // Resolve proxy info. | |
832 rv = session_->proxy_service()->ResolveProxy(request_->url, | |
833 &proxy_info_, | |
834 &proxy_callback_, | |
835 &pac_request_); | |
836 if (rv == ERR_IO_PENDING) { | |
837 session_callback_->set_load_state( | |
838 LOAD_STATE_RESOLVING_PROXY_FOR_URL); | |
839 } | |
840 } | |
841 | |
842 if (rv == OK) | |
843 rv = DidResolveProxy(); // calls OpenRequest and SendRequest | |
844 | |
845 if (rv == ERR_IO_PENDING) { | |
846 session_callback_->AddRef(); // balanced when callback runs or from | |
847 // OnProxyInfoAvailable. | |
848 callback_ = callback; | |
849 } | |
850 | |
851 return rv; | |
852 } | |
853 | |
854 int HttpTransactionWinHttp::RestartIgnoringLastError( | |
855 CompletionCallback* callback) { | |
856 int flags = load_flags_; | |
857 | |
858 // Depending on the error, we make different adjustments to our load flags. | |
859 // We DCHECK that we shouldn't already have ignored this error. | |
860 switch (last_error_) { | |
861 case ERROR_WINHTTP_SECURE_CERT_CN_INVALID: | |
862 DCHECK(!(flags & LOAD_IGNORE_CERT_COMMON_NAME_INVALID)); | |
863 flags |= LOAD_IGNORE_CERT_COMMON_NAME_INVALID; | |
864 break; | |
865 case ERROR_WINHTTP_SECURE_CERT_DATE_INVALID: | |
866 DCHECK(!(flags & LOAD_IGNORE_CERT_DATE_INVALID)); | |
867 flags |= LOAD_IGNORE_CERT_DATE_INVALID; | |
868 break; | |
869 case ERROR_WINHTTP_SECURE_INVALID_CA: | |
870 DCHECK(!(flags & LOAD_IGNORE_CERT_AUTHORITY_INVALID)); | |
871 flags |= LOAD_IGNORE_CERT_AUTHORITY_INVALID; | |
872 break; | |
873 case ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE: | |
874 DCHECK(!(flags & LOAD_IGNORE_CERT_WRONG_USAGE)); | |
875 flags |= LOAD_IGNORE_CERT_WRONG_USAGE; | |
876 break; | |
877 case ERROR_WINHTTP_SECURE_CERT_REV_FAILED: { | |
878 DCHECK(!(flags & LOAD_IGNORE_CERT_REVOCATION)); | |
879 flags |= LOAD_IGNORE_CERT_REVOCATION; | |
880 // WinHTTP doesn't have a SECURITY_FLAG_IGNORE_CERT_REV_FAILED flag | |
881 // and doesn't let us undo WINHTTP_ENABLE_SSL_REVOCATION. The only | |
882 // way to ignore this error is to open a new request without enabling | |
883 // WINHTTP_ENABLE_SSL_REVOCATION. | |
884 if (!ReopenRequest()) | |
885 return TranslateLastOSError(); | |
886 break; | |
887 } | |
888 // We can't instruct WinHttp to recover from these errors. No choice | |
889 // but to cancel the request. | |
890 case ERROR_WINHTTP_SECURE_CHANNEL_ERROR: | |
891 case ERROR_WINHTTP_SECURE_INVALID_CERT: | |
892 case ERROR_WINHTTP_SECURE_CERT_REVOKED: | |
893 // We don't knows how to continue from here. | |
894 default: | |
895 LOG(ERROR) << "Unable to restart the HTTP transaction ignoring " | |
896 "the error " << last_error_; | |
897 return ERR_ABORTED; | |
898 } | |
899 | |
900 // Update the load flags to ignore the specified error. | |
901 load_flags_ = flags; | |
902 | |
903 return Restart(callback); | |
904 } | |
905 | |
906 int HttpTransactionWinHttp::RestartWithAuth( | |
907 const std::wstring& username, | |
908 const std::wstring& password, | |
909 CompletionCallback* callback) { | |
910 DCHECK(proxy_auth_ && proxy_auth_->state == AUTH_STATE_NEED_AUTH || | |
911 server_auth_ && server_auth_->state == AUTH_STATE_NEED_AUTH); | |
912 | |
913 // Proxy gets set first, then WWW. | |
914 AuthData* auth = | |
915 proxy_auth_ && proxy_auth_->state == AUTH_STATE_NEED_AUTH ? | |
916 proxy_auth_ : server_auth_; | |
917 | |
918 if (auth) { | |
919 auth->state = AUTH_STATE_HAVE_AUTH; | |
920 auth->username = username; | |
921 auth->password = password; | |
922 } | |
923 | |
924 return Restart(callback); | |
925 } | |
926 | |
927 // The code common to RestartIgnoringLastError and RestartWithAuth. | |
928 int HttpTransactionWinHttp::Restart(CompletionCallback* callback) { | |
929 DCHECK(callback); | |
930 | |
931 // ensure that we only have one asynchronous call at a time. | |
932 DCHECK(!callback_); | |
933 | |
934 int rv = RestartInternal(); | |
935 if (rv != ERR_IO_PENDING) | |
936 return rv; | |
937 | |
938 session_callback_->AddRef(); // balanced when callback runs. | |
939 | |
940 callback_ = callback; | |
941 return ERR_IO_PENDING; | |
942 } | |
943 | |
944 // If HttpTransactionWinHttp needs to restart itself after handling an error, | |
945 // it calls this method. This method leaves callback_ unchanged. The caller | |
946 // is responsible for calling session_callback_->AddRef() if this method | |
947 // returns ERR_IO_PENDING. | |
948 int HttpTransactionWinHttp::RestartInternal() { | |
949 content_length_remaining_ = -1; | |
950 upload_progress_ = 0; | |
951 | |
952 return SendRequest(); | |
953 } | |
954 | |
955 // We use WinHttpQueryDataAvailable rather than pure async read to trade | |
956 // a better latency for a decreased throughput. We'll make more IO calls, | |
957 // and thus use more CPU for a given transaction by using | |
958 // WinHttpQueryDataAvailable, but it allows us to get a faster response | |
959 // time to the app for data, which is more important. | |
960 int HttpTransactionWinHttp::Read(char* buf, int buf_len, | |
961 CompletionCallback* callback) { | |
962 DCHECK(buf); | |
963 DCHECK(buf_len > 0); | |
964 DCHECK(callback); | |
965 | |
966 DCHECK(!callback_); | |
967 DCHECK(request_handle_); | |
968 | |
969 // If we have already received the full response, then we know we are done. | |
970 if (content_length_remaining_ == 0) { | |
971 LogTransactionMetrics(); | |
972 return 0; | |
973 } | |
974 | |
975 session_callback_->set_read_buf(buf); | |
976 session_callback_->set_read_buf_len(buf_len); | |
977 | |
978 // We must consume all the available data reported by the previous | |
979 // WinHttpQueryDataAvailable call before we can call | |
980 // WinHttpQueryDataAvailable again. | |
981 BOOL ok; | |
982 if (session_callback_->bytes_available()) { | |
983 ok = session_callback_->ReadData(request_handle_); | |
984 } else { | |
985 ok = WinHttpQueryDataAvailable(request_handle_, NULL); | |
986 } | |
987 if (!ok) | |
988 return TranslateLastOSError(); | |
989 | |
990 session_callback_->set_load_state(LOAD_STATE_READING_RESPONSE); | |
991 session_callback_->AddRef(); // balanced when callback runs. | |
992 need_to_wait_for_handle_closing_ = true; | |
993 | |
994 callback_ = callback; | |
995 return ERR_IO_PENDING; | |
996 } | |
997 | |
998 const HttpResponseInfo* HttpTransactionWinHttp::GetResponseInfo() const { | |
999 return (response_.headers || response_.ssl_info.cert) ? &response_ : NULL; | |
1000 } | |
1001 | |
1002 LoadState HttpTransactionWinHttp::GetLoadState() const { | |
1003 return session_callback_->load_state(); | |
1004 } | |
1005 | |
1006 uint64 HttpTransactionWinHttp::GetUploadProgress() const { | |
1007 return upload_progress_; | |
1008 } | |
1009 | |
1010 void HttpTransactionWinHttp::DoCallback(int rv) { | |
1011 DCHECK(rv != ERR_IO_PENDING); | |
1012 DCHECK(callback_); | |
1013 | |
1014 // since Run may result in Read being called, clear callback_ up front. | |
1015 CompletionCallback* c = callback_; | |
1016 callback_ = NULL; | |
1017 c->Run(rv); | |
1018 } | |
1019 | |
1020 bool HttpTransactionWinHttp::OpenRequest() { | |
1021 DCHECK(!connect_handle_); | |
1022 DCHECK(!request_handle_); | |
1023 | |
1024 const GURL& url = request_->url; | |
1025 const std::string& scheme = url.scheme(); | |
1026 | |
1027 // Flags passed to WinHttpOpenRequest. Disable any conversion WinHttp | |
1028 // might perform on our URL string. We handle the escaping ourselves. | |
1029 DWORD open_flags = WINHTTP_FLAG_ESCAPE_DISABLE | | |
1030 WINHTTP_FLAG_ESCAPE_DISABLE_QUERY | | |
1031 WINHTTP_FLAG_NULL_CODEPAGE; | |
1032 | |
1033 // We should only be dealing with HTTP at this point: | |
1034 DCHECK(LowerCaseEqualsASCII(scheme, "http") || | |
1035 LowerCaseEqualsASCII(scheme, "https")); | |
1036 | |
1037 int in_port = url.IntPort(); | |
1038 DCHECK(in_port != url_parse::PORT_INVALID) << | |
1039 "Valid URLs should have valid ports"; | |
1040 | |
1041 // Map to port numbers that Windows expects. | |
1042 INTERNET_PORT port = in_port; | |
1043 if (LowerCaseEqualsASCII(scheme, "https")) { | |
1044 is_https_ = true; | |
1045 open_flags |= WINHTTP_FLAG_SECURE; | |
1046 if (in_port == url_parse::PORT_UNSPECIFIED) | |
1047 port = INTERNET_DEFAULT_HTTPS_PORT; | |
1048 } else { | |
1049 if (in_port == url_parse::PORT_UNSPECIFIED) | |
1050 port = INTERNET_DEFAULT_HTTP_PORT; | |
1051 } | |
1052 | |
1053 const std::string& host = url.host(); | |
1054 | |
1055 // Use the primary session handle unless we are talking to a TLS-intolerant | |
1056 // server. | |
1057 // | |
1058 // Since the SSL protocol versions enabled are an option of a session | |
1059 // handle, supporting TLS-intolerant servers unfortunately requires opening | |
1060 // an alternative session in which TLS 1.0 is disabled. | |
1061 if (!session_->internet() && !session_->Init(request_->user_agent)) { | |
1062 DLOG(ERROR) << "unable to create the internet"; | |
1063 return false; | |
1064 } | |
1065 HINTERNET internet = session_->internet(); | |
1066 if (is_tls_intolerant_) { | |
1067 if (!session_->internet_no_tls() && | |
1068 !session_->InitNoTLS(request_->user_agent)) { | |
1069 DLOG(ERROR) << "unable to create the no-TLS alternative internet"; | |
1070 return false; | |
1071 } | |
1072 internet = session_->internet_no_tls(); | |
1073 } | |
1074 | |
1075 // This function operates synchronously. | |
1076 connect_handle_ = | |
1077 WinHttpConnect(internet, ASCIIToWide(host).c_str(), port, 0); | |
1078 if (!connect_handle_) { | |
1079 DLOG(ERROR) << "WinHttpConnect failed: " << GetLastError(); | |
1080 return false; | |
1081 } | |
1082 | |
1083 std::string request_path = url.PathForRequest(); | |
1084 | |
1085 // This function operates synchronously. | |
1086 request_handle_ = | |
1087 WinHttpOpenRequest(connect_handle_, | |
1088 ASCIIToWide(request_->method).c_str(), | |
1089 ASCIIToWide(request_path).c_str(), | |
1090 NULL, // use HTTP/1.1 | |
1091 WINHTTP_NO_REFERER, // none | |
1092 WINHTTP_DEFAULT_ACCEPT_TYPES, // none | |
1093 open_flags); | |
1094 if (!request_handle_) { | |
1095 DLOG(ERROR) << "WinHttpOpenRequest failed: " << GetLastError(); | |
1096 return false; | |
1097 } | |
1098 | |
1099 // TODO(darin): we may wish to prune-back the set of notifications we receive | |
1100 WINHTTP_STATUS_CALLBACK old_callback = WinHttpSetStatusCallback( | |
1101 request_handle_, StatusCallback, | |
1102 WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL); | |
1103 DCHECK(old_callback == NULL); | |
1104 if (old_callback == WINHTTP_INVALID_STATUS_CALLBACK) { | |
1105 DLOG(ERROR) << "WinHttpSetStatusCallback failed:" << GetLastError(); | |
1106 return false; | |
1107 } | |
1108 | |
1109 DWORD_PTR ctx = reinterpret_cast<DWORD_PTR>(session_callback_.get()); | |
1110 if (!WinHttpSetOption(request_handle_, WINHTTP_OPTION_CONTEXT_VALUE, | |
1111 &ctx, sizeof(ctx))) { | |
1112 DLOG(ERROR) << "WinHttpSetOption context value failed:" << GetLastError(); | |
1113 return false; | |
1114 } | |
1115 | |
1116 // We just associated a status callback context value with the request | |
1117 // handle. | |
1118 session_callback_->AddRef(); // balanced in OnHandleClosing | |
1119 session_->AddRefBySessionCallback(); | |
1120 | |
1121 // We have our own cookie and redirect management. | |
1122 DWORD options = WINHTTP_DISABLE_COOKIES | | |
1123 WINHTTP_DISABLE_REDIRECTS; | |
1124 | |
1125 if (!WinHttpSetOption(request_handle_, WINHTTP_OPTION_DISABLE_FEATURE, | |
1126 &options, sizeof(options))) { | |
1127 DLOG(ERROR) << "WinHttpSetOption disable feature failed:" << GetLastError(); | |
1128 return false; | |
1129 } | |
1130 | |
1131 // Disable auto-login for Negotiate and NTLM auth methods. | |
1132 DWORD security_level = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH; | |
1133 if (!WinHttpSetOption(request_handle_, WINHTTP_OPTION_AUTOLOGON_POLICY, | |
1134 &security_level, sizeof(security_level))) { | |
1135 DLOG(ERROR) << "WinHttpSetOption autologon failed: " << GetLastError(); | |
1136 return false; | |
1137 } | |
1138 | |
1139 // Add request headers. WinHttp is known to convert the headers to bytes | |
1140 // using the system charset converter, so we use the same converter to map | |
1141 // our request headers to UTF-16 before handing the data to WinHttp. | |
1142 std::wstring request_headers = base::SysNativeMBToWide(GetRequestHeaders()); | |
1143 | |
1144 DWORD len = static_cast<DWORD>(request_headers.size()); | |
1145 if (!WinHttpAddRequestHeaders(request_handle_, | |
1146 request_headers.c_str(), | |
1147 len, | |
1148 WINHTTP_ADDREQ_FLAG_ADD | | |
1149 WINHTTP_ADDREQ_FLAG_REPLACE)) { | |
1150 DLOG(ERROR) << "WinHttpAddRequestHeaders failed: " << GetLastError(); | |
1151 return false; | |
1152 } | |
1153 | |
1154 return true; | |
1155 } | |
1156 | |
1157 int HttpTransactionWinHttp::SendRequest() { | |
1158 DCHECK(request_handle_); | |
1159 | |
1160 // Apply any authentication (username/password) we might have. | |
1161 ApplyAuth(); | |
1162 | |
1163 // Apply any proxy info. | |
1164 proxy_info_.Apply(request_handle_); | |
1165 | |
1166 // Check SSL server certificate revocation. | |
1167 if (is_https_) { | |
1168 bool ignore_cert_rev = (load_flags_ & LOAD_IGNORE_CERT_REVOCATION) != 0; | |
1169 GURL origin = request_->url.GetOrigin(); | |
1170 const std::string& origin_spec = origin.spec(); | |
1171 if (ignore_cert_rev) | |
1172 session_->IgnoreCertRev(origin_spec); | |
1173 else if (session_->ShouldIgnoreCertRev(origin_spec)) | |
1174 ignore_cert_rev = true; | |
1175 | |
1176 if (session_->rev_checking_enabled() && !ignore_cert_rev) { | |
1177 DWORD options = WINHTTP_ENABLE_SSL_REVOCATION; | |
1178 if (!WinHttpSetOption(request_handle_, WINHTTP_OPTION_ENABLE_FEATURE, | |
1179 &options, sizeof(options))) { | |
1180 DLOG(ERROR) << "WinHttpSetOption failed: " << GetLastError(); | |
1181 return TranslateLastOSError(); | |
1182 } | |
1183 rev_checking_enabled_ = true; | |
1184 } | |
1185 } | |
1186 | |
1187 const int kCertFlags = LOAD_IGNORE_CERT_COMMON_NAME_INVALID | | |
1188 LOAD_IGNORE_CERT_DATE_INVALID | | |
1189 LOAD_IGNORE_CERT_AUTHORITY_INVALID | | |
1190 LOAD_IGNORE_CERT_WRONG_USAGE; | |
1191 | |
1192 if (load_flags_ & kCertFlags) { | |
1193 DWORD security_flags; | |
1194 DWORD length = sizeof(security_flags); | |
1195 | |
1196 if (!WinHttpQueryOption(request_handle_, | |
1197 WINHTTP_OPTION_SECURITY_FLAGS, | |
1198 &security_flags, | |
1199 &length)) { | |
1200 NOTREACHED() << "WinHttpQueryOption failed."; | |
1201 return TranslateLastOSError(); | |
1202 } | |
1203 | |
1204 // On Vista, WinHttpSetOption() fails with an incorrect parameter error. | |
1205 // WinHttpQueryOption() sets an undocumented flag (0x01000000, which seems | |
1206 // to be a query-only flag) in security_flags that causes this error. To | |
1207 // work-around it, we only keep the documented error flags. | |
1208 security_flags &= (SECURITY_FLAG_IGNORE_UNKNOWN_CA | | |
1209 SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | | |
1210 SECURITY_FLAG_IGNORE_CERT_CN_INVALID | | |
1211 SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE); | |
1212 | |
1213 if (load_flags_ & LOAD_IGNORE_CERT_COMMON_NAME_INVALID) | |
1214 security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID; | |
1215 | |
1216 if (load_flags_ & LOAD_IGNORE_CERT_DATE_INVALID) | |
1217 security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID; | |
1218 | |
1219 if (load_flags_ & LOAD_IGNORE_CERT_AUTHORITY_INVALID) | |
1220 security_flags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA; | |
1221 | |
1222 if (load_flags_ & LOAD_IGNORE_CERT_WRONG_USAGE) | |
1223 security_flags |= SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE; | |
1224 | |
1225 if (!WinHttpSetOption(request_handle_, | |
1226 WINHTTP_OPTION_SECURITY_FLAGS, | |
1227 &security_flags, | |
1228 sizeof(security_flags))) { | |
1229 NOTREACHED() << "WinHttpSetOption failed."; | |
1230 return TranslateLastOSError(); | |
1231 } | |
1232 } | |
1233 | |
1234 response_.request_time = Time::Now(); | |
1235 response_.was_cached = false; | |
1236 | |
1237 DWORD total_size = 0; | |
1238 if (request_->upload_data) { | |
1239 upload_stream_.reset(new UploadDataStream(request_->upload_data)); | |
1240 uint64 upload_len = upload_stream_->size(); | |
1241 if (upload_len == 0) { | |
1242 upload_stream_.reset(); | |
1243 } else { | |
1244 // TODO(darin): no way to support >4GB uploads w/ WinHttp? | |
1245 if (upload_len > static_cast<uint64>(DWORD(-1))) { | |
1246 NOTREACHED() << "upload length is too large"; | |
1247 return ERR_FILE_TOO_BIG; | |
1248 } | |
1249 | |
1250 total_size = static_cast<DWORD>(upload_len); | |
1251 } | |
1252 } | |
1253 | |
1254 if (request_submitted_) { | |
1255 request_submitted_ = false; | |
1256 session_->request_throttle()->NotifyRequestDone(connect_peer_); | |
1257 } | |
1258 if (proxy_info_.is_direct()) | |
1259 connect_peer_ = request_->url.GetOrigin().spec(); | |
1260 else | |
1261 connect_peer_ = proxy_info_.proxy_server(); | |
1262 DWORD_PTR ctx = reinterpret_cast<DWORD_PTR>(session_callback_.get()); | |
1263 if (!session_->request_throttle()->SubmitRequest(connect_peer_, | |
1264 request_handle_, | |
1265 total_size, ctx)) { | |
1266 last_error_ = GetLastError(); | |
1267 DLOG(ERROR) << "WinHttpSendRequest failed: " << last_error_; | |
1268 return TranslateOSError(last_error_); | |
1269 } | |
1270 | |
1271 request_submitted_ = true; | |
1272 return ERR_IO_PENDING; | |
1273 } | |
1274 | |
1275 // Called after certain failures of SendRequest to reset the members opened | |
1276 // or modified in OpenRequest and SendRequest and call OpenRequest again. | |
1277 bool HttpTransactionWinHttp::ReopenRequest() { | |
1278 DCHECK(connect_handle_); | |
1279 DCHECK(request_handle_); | |
1280 | |
1281 session_callback_->set_handle_closing_event( | |
1282 session_->handle_closing_event()); | |
1283 WinHttpCloseHandle(request_handle_); | |
1284 WaitForSingleObject(session_->handle_closing_event(), INFINITE); | |
1285 request_handle_ = NULL; | |
1286 WinHttpCloseHandle(connect_handle_); | |
1287 connect_handle_ = NULL; | |
1288 session_callback_->ResetForNewRequest(); | |
1289 | |
1290 // Don't need to reset is_https_, rev_checking_enabled_, and | |
1291 // response_.request_time. | |
1292 | |
1293 return OpenRequest(); | |
1294 } | |
1295 | |
1296 int HttpTransactionWinHttp::DidResolveProxy() { | |
1297 // We may already have a request handle if we are changing proxy config. | |
1298 if (!(request_handle_ ? ReopenRequest() : OpenRequest())) | |
1299 return TranslateLastOSError(); | |
1300 | |
1301 return SendRequest(); | |
1302 } | |
1303 | |
1304 int HttpTransactionWinHttp::DidReceiveError(DWORD error, | |
1305 DWORD secure_failure) { | |
1306 DCHECK(error != ERROR_SUCCESS); | |
1307 | |
1308 session_callback_->set_load_state(LOAD_STATE_IDLE); | |
1309 need_to_wait_for_handle_closing_ = false; | |
1310 | |
1311 int rv; | |
1312 | |
1313 if (error == ERROR_WINHTTP_RESEND_REQUEST) | |
1314 return RestartInternal(); | |
1315 | |
1316 if (error == ERROR_WINHTTP_NAME_NOT_RESOLVED || | |
1317 error == ERROR_WINHTTP_CANNOT_CONNECT || | |
1318 error == ERROR_WINHTTP_TIMEOUT) { | |
1319 // These errors may have been caused by a proxy configuration error, or | |
1320 // rather they may go away by trying a different proxy config! If we have | |
1321 // an explicit proxy config, then we just have to report an error. | |
1322 if (!have_proxy_info_) { | |
1323 rv = session_->proxy_service()->ReconsiderProxyAfterError( | |
1324 request_->url, &proxy_info_, &proxy_callback_, &pac_request_); | |
1325 if (rv == OK) // got new proxy info to try | |
1326 return DidResolveProxy(); | |
1327 if (rv == ERR_IO_PENDING) // waiting to resolve proxy info | |
1328 return rv; | |
1329 // else, fall through and just report an error. | |
1330 } | |
1331 } | |
1332 | |
1333 if (error == ERROR_WINHTTP_SECURE_FAILURE) { | |
1334 DWORD filtered_secure_failure = FilterSecureFailure(secure_failure, | |
1335 load_flags_); | |
1336 // If load_flags_ ignores all the errors in secure_failure, we shouldn't | |
1337 // get the ERROR_WINHTTP_SECURE_FAILURE error. | |
1338 DCHECK(filtered_secure_failure || !secure_failure); | |
1339 error = MapSecureFailureToError(filtered_secure_failure); | |
1340 } | |
1341 | |
1342 last_error_ = error; | |
1343 rv = TranslateOSError(error); | |
1344 | |
1345 if ((rv == ERR_SSL_PROTOCOL_ERROR || | |
1346 rv == ERR_SSL_VERSION_OR_CIPHER_MISMATCH) && | |
1347 !session_callback_->request_was_probably_sent() && | |
1348 session_->tls_enabled() && !is_tls_intolerant_) { | |
1349 // The server might be TLS intolerant. Or it might be an SSL 3.0 server | |
1350 // that chose a TLS-only cipher suite, which we handle in the same way. | |
1351 // Downgrade to SSL 3.0 and retry. | |
1352 is_tls_intolerant_ = true; | |
1353 if (!ReopenRequest()) | |
1354 return TranslateLastOSError(); | |
1355 return RestartInternal(); | |
1356 } | |
1357 if (rv == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { | |
1358 // TODO(wtc): Bug 1230409: We don't support SSL client authentication yet. | |
1359 // For now we set a null client certificate, which works on XP SP3, Vista | |
1360 // and later. On XP SP2 and below, this fails with ERROR_INVALID_PARAMETER | |
1361 // (87). This allows us to access servers that request but do not require | |
1362 // client certificates. | |
1363 if (WinHttpSetOption(request_handle_, | |
1364 WINHTTP_OPTION_CLIENT_CERT_CONTEXT, | |
1365 WINHTTP_NO_CLIENT_CERT_CONTEXT, 0)) | |
1366 return RestartInternal(); | |
1367 } | |
1368 if (IsCertificateError(rv)) { | |
1369 response_.ssl_info.cert = GetServerCertificate(); | |
1370 response_.ssl_info.cert_status = | |
1371 MapSecureFailureToCertStatus(secure_failure); | |
1372 CertStatusCache* cert_status_cache = session_->cert_status_cache(); | |
1373 cert_status_cache->SetCertStatus(*response_.ssl_info.cert, | |
1374 request_->url.host(), | |
1375 response_.ssl_info.cert_status); | |
1376 } | |
1377 | |
1378 return rv; | |
1379 } | |
1380 | |
1381 int HttpTransactionWinHttp::DidSendRequest() { | |
1382 BOOL ok; | |
1383 if (upload_stream_.get() && upload_stream_->buf_len() > 0) { | |
1384 // write upload data | |
1385 DWORD buf_len = static_cast<DWORD>(upload_stream_->buf_len()); | |
1386 ok = WinHttpWriteData(request_handle_, | |
1387 upload_stream_->buf(), | |
1388 buf_len, | |
1389 NULL); | |
1390 if (ok) | |
1391 need_to_wait_for_handle_closing_ = true; | |
1392 } else { | |
1393 upload_stream_.reset(); | |
1394 need_to_wait_for_handle_closing_ = false; | |
1395 | |
1396 // begin receiving the response | |
1397 ok = WinHttpReceiveResponse(request_handle_, NULL); | |
1398 } | |
1399 return ok ? ERR_IO_PENDING : TranslateLastOSError(); | |
1400 } | |
1401 | |
1402 int HttpTransactionWinHttp::DidWriteData(DWORD num_bytes) { | |
1403 DCHECK(upload_stream_.get()); | |
1404 DCHECK(num_bytes > 0); | |
1405 | |
1406 upload_stream_->DidConsume(num_bytes); | |
1407 upload_progress_ = upload_stream_->position(); | |
1408 | |
1409 // OK, we are ready to start receiving the response. The code in | |
1410 // DidSendRequest does exactly what we want! | |
1411 return DidSendRequest(); | |
1412 } | |
1413 | |
1414 int HttpTransactionWinHttp::DidReadData(DWORD num_bytes) { | |
1415 int rv = static_cast<int>(num_bytes); | |
1416 DCHECK(rv >= 0); | |
1417 | |
1418 session_callback_->set_load_state(LOAD_STATE_IDLE); | |
1419 session_callback_->reduce_bytes_available(rv); | |
1420 need_to_wait_for_handle_closing_ = false; | |
1421 | |
1422 if (content_length_remaining_ > 0) { | |
1423 content_length_remaining_ -= rv; | |
1424 | |
1425 // HTTP/1.0 servers are known to send more data than they report in their | |
1426 // Content-Length header (in the non-keepalive case). IE and Moz both | |
1427 // tolerate this situation, and therefore so must we. | |
1428 if (content_length_remaining_ < 0) | |
1429 content_length_remaining_ = 0; | |
1430 } | |
1431 | |
1432 // We have read the entire response. Mark the request done to unblock a | |
1433 // queued request. | |
1434 if (rv == 0) { | |
1435 LogTransactionMetrics(); | |
1436 DCHECK(request_submitted_); | |
1437 request_submitted_ = false; | |
1438 session_->request_throttle()->NotifyRequestDone(connect_peer_); | |
1439 } | |
1440 | |
1441 return rv; | |
1442 } | |
1443 | |
1444 void HttpTransactionWinHttp::LogTransactionMetrics() const { | |
1445 base::TimeDelta duration = base::Time::Now() - response_.request_time; | |
1446 if (60 < duration.InMinutes()) | |
1447 return; | |
1448 UMA_HISTOGRAM_LONG_TIMES(L"Net.Transaction_Latency_WinHTTP", duration); | |
1449 } | |
1450 | |
1451 int HttpTransactionWinHttp::DidReceiveHeaders() { | |
1452 session_callback_->set_load_state(LOAD_STATE_IDLE); | |
1453 | |
1454 DWORD size = 0; | |
1455 if (!WinHttpQueryHeaders(request_handle_, | |
1456 WINHTTP_QUERY_RAW_HEADERS, | |
1457 WINHTTP_HEADER_NAME_BY_INDEX, | |
1458 NULL, | |
1459 &size, | |
1460 WINHTTP_NO_HEADER_INDEX)) { | |
1461 DWORD error = GetLastError(); | |
1462 if (error != ERROR_INSUFFICIENT_BUFFER) { | |
1463 DLOG(ERROR) << "WinHttpQueryHeaders failed: " << GetLastError(); | |
1464 return TranslateLastOSError(); | |
1465 } | |
1466 // OK, size should tell us how much to allocate... | |
1467 DCHECK(size > 0); | |
1468 } | |
1469 | |
1470 std::wstring raw_headers; | |
1471 | |
1472 // 'size' is the number of bytes rather than the number of characters. | |
1473 DCHECK(size % 2 == 0); | |
1474 if (!WinHttpQueryHeaders(request_handle_, | |
1475 WINHTTP_QUERY_RAW_HEADERS, | |
1476 WINHTTP_HEADER_NAME_BY_INDEX, | |
1477 WriteInto(&raw_headers, size/2 + 1), | |
1478 &size, | |
1479 WINHTTP_NO_HEADER_INDEX)) { | |
1480 DLOG(ERROR) << "WinHttpQueryHeaders failed: " << GetLastError(); | |
1481 return TranslateLastOSError(); | |
1482 } | |
1483 | |
1484 response_.response_time = Time::Now(); | |
1485 | |
1486 // From experimentation, it appears that WinHttp translates non-ASCII bytes | |
1487 // found in the response headers to UTF-16 assuming that they are encoded | |
1488 // using the default system charset. We attempt to undo that here. | |
1489 response_.headers = | |
1490 new HttpResponseHeaders(base::SysWideToNativeMB(raw_headers)); | |
1491 | |
1492 // WinHTTP truncates a response longer than 2GB. Perhaps it stores the | |
1493 // response's content length in a signed 32-bit integer. We fail rather | |
1494 // than reading a truncated response. | |
1495 if (response_.headers->GetContentLength() > 0x80000000) | |
1496 return ERR_FILE_TOO_BIG; | |
1497 | |
1498 response_.vary_data.Init(*request_, *response_.headers); | |
1499 int rv = PopulateAuthChallenge(); | |
1500 if (rv != OK) | |
1501 return rv; | |
1502 | |
1503 // Unfortunately, WinHttp does not close the connection when a non-keepalive | |
1504 // response is _not_ followed by the server closing the connection. So, we | |
1505 // attempt to hack around this bug. | |
1506 if (!response_.headers->IsKeepAlive()) | |
1507 content_length_remaining_ = response_.headers->GetContentLength(); | |
1508 | |
1509 return OK; | |
1510 } | |
1511 | |
1512 // Populates response_.auth_challenge with the authentication challenge info. | |
1513 int HttpTransactionWinHttp::PopulateAuthChallenge() { | |
1514 DCHECK(response_.headers); | |
1515 | |
1516 int status = response_.headers->response_code(); | |
1517 if (status != 401 && status != 407) | |
1518 return OK; | |
1519 | |
1520 scoped_refptr<AuthChallengeInfo> auth_info = new AuthChallengeInfo; | |
1521 | |
1522 auth_info->is_proxy = (status == 407); | |
1523 | |
1524 if (auth_info->is_proxy) { | |
1525 // TODO(wtc): get the proxy server host from proxy_info_. | |
1526 // TODO(wtc): internationalize? | |
1527 auth_info->host = L"proxy"; | |
1528 } else { | |
1529 auth_info->host = ASCIIToWide(request_->url.host()); | |
1530 } | |
1531 | |
1532 // Here we're checking only the first *-Authenticate header. When a server | |
1533 // responds with multiple methods, we use the first. | |
1534 // TODO(wtc): Bug 1124614: look at all the authentication methods and pick | |
1535 // the best one that we support. failover to other authentication methods. | |
1536 std::string header_value; | |
1537 std::string header_name = auth_info->is_proxy ? | |
1538 "Proxy-Authenticate" : "WWW-Authenticate"; | |
1539 if (!response_.headers->EnumerateHeader(NULL, header_name, &header_value)) | |
1540 return OK; | |
1541 | |
1542 // TODO(darin): Need to support RFC 2047 encoded realm strings. For now, we | |
1543 // limit our support to ASCII and "native code page" realm strings. | |
1544 std::wstring auth_header = base::SysNativeMBToWide(header_value); | |
1545 | |
1546 // auth_header is a string which looks like: | |
1547 // Digest realm="The Awesome Site", domain="/page.html", ... | |
1548 std::wstring::const_iterator space = find(auth_header.begin(), | |
1549 auth_header.end(), L' '); | |
1550 auth_info->scheme.assign(auth_header.begin(), space); | |
1551 auth_info->realm = GetHeaderParamValue(auth_header, L"realm"); | |
1552 | |
1553 // Now auth_info has been fully populated. Before we swap it with | |
1554 // response_.auth_challenge, update the auth cache key and remove any | |
1555 // presumably incorrect auth data in the auth cache. | |
1556 std::string* auth_cache_key; | |
1557 AuthData* auth; | |
1558 if (auth_info->is_proxy) { | |
1559 if (!proxy_auth_) | |
1560 proxy_auth_ = new AuthData; | |
1561 auth = proxy_auth_; | |
1562 auth_cache_key = &proxy_auth_cache_key_; | |
1563 } else { | |
1564 if (!server_auth_) | |
1565 server_auth_ = new AuthData; | |
1566 auth = server_auth_; | |
1567 auth_cache_key = &server_auth_cache_key_; | |
1568 } | |
1569 *auth_cache_key = AuthCache::HttpKey(request_->url, *auth_info); | |
1570 DCHECK(!auth_cache_key->empty()); | |
1571 auth->scheme = auth_info->scheme; | |
1572 if (auth->state == AUTH_STATE_HAVE_AUTH) { | |
1573 // Remove the cache entry for the credentials we just failed on. | |
1574 // Note: we require the username/password to match before removing | |
1575 // since the entry in the cache may be newer than what we used last time. | |
1576 AuthData* cached_auth = session_->auth_cache()->Lookup(*auth_cache_key); | |
1577 if (cached_auth && cached_auth->username == auth->username && | |
1578 cached_auth->password == auth->password) | |
1579 session_->auth_cache()->Remove(*auth_cache_key); | |
1580 auth->state = AUTH_STATE_NEED_AUTH; | |
1581 } | |
1582 DCHECK(auth->state == AUTH_STATE_NEED_AUTH); | |
1583 | |
1584 // Try to use the username/password embedded in the URL first. | |
1585 // (By checking !used_embedded_credentials_, we make sure that this | |
1586 // is only done once for the transaction.) | |
1587 if (!auth_info->is_proxy && request_->url.has_username() && | |
1588 !used_embedded_credentials_) { | |
1589 // TODO(wtc) It may be necessary to unescape the username and password | |
1590 // after extracting them from the URL. We should be careful about | |
1591 // embedded nulls in that case. | |
1592 used_embedded_credentials_ = true; | |
1593 auth->state = AUTH_STATE_HAVE_AUTH; | |
1594 auth->username = ASCIIToWide(request_->url.username()); | |
1595 auth->password = ASCIIToWide(request_->url.password()); | |
1596 return RestartInternal(); | |
1597 } | |
1598 | |
1599 // Check the auth cache for an entry. | |
1600 AuthData* cached_auth = session_->auth_cache()->Lookup(*auth_cache_key); | |
1601 if (cached_auth) { | |
1602 auth->state = AUTH_STATE_HAVE_AUTH; | |
1603 auth->username = cached_auth->username; | |
1604 auth->password = cached_auth->password; | |
1605 return RestartInternal(); | |
1606 } | |
1607 | |
1608 response_.auth_challenge.swap(auth_info); | |
1609 return OK; | |
1610 } | |
1611 | |
1612 static DWORD StringToAuthScheme(const std::wstring& scheme) { | |
1613 if (LowerCaseEqualsASCII(scheme, "basic")) | |
1614 return WINHTTP_AUTH_SCHEME_BASIC; | |
1615 if (LowerCaseEqualsASCII(scheme, "digest")) | |
1616 return WINHTTP_AUTH_SCHEME_DIGEST; | |
1617 if (LowerCaseEqualsASCII(scheme, "ntlm")) | |
1618 return WINHTTP_AUTH_SCHEME_NTLM; | |
1619 if (LowerCaseEqualsASCII(scheme, "negotiate")) | |
1620 return WINHTTP_AUTH_SCHEME_NEGOTIATE; | |
1621 if (LowerCaseEqualsASCII(scheme, "passport1.4")) | |
1622 return WINHTTP_AUTH_SCHEME_PASSPORT; | |
1623 return 0; | |
1624 } | |
1625 | |
1626 // Applies authentication credentials to request_handle_. | |
1627 void HttpTransactionWinHttp::ApplyAuth() { | |
1628 DWORD auth_scheme; | |
1629 BOOL rv; | |
1630 if (proxy_auth_ && proxy_auth_->state == AUTH_STATE_HAVE_AUTH) { | |
1631 // Add auth data to cache. | |
1632 DCHECK(!proxy_auth_cache_key_.empty()); | |
1633 session_->auth_cache()->Add(proxy_auth_cache_key_, proxy_auth_); | |
1634 auth_scheme = StringToAuthScheme(proxy_auth_->scheme); | |
1635 if (auth_scheme == 0) | |
1636 return; | |
1637 | |
1638 rv = WinHttpSetCredentials(request_handle_, | |
1639 WINHTTP_AUTH_TARGET_PROXY, | |
1640 auth_scheme, | |
1641 proxy_auth_->username.c_str(), | |
1642 proxy_auth_->password.c_str(), | |
1643 NULL); | |
1644 } | |
1645 | |
1646 if (server_auth_ && server_auth_->state == AUTH_STATE_HAVE_AUTH) { | |
1647 // Add auth data to cache. | |
1648 DCHECK(!server_auth_cache_key_.empty()); | |
1649 session_->auth_cache()->Add(server_auth_cache_key_, server_auth_); | |
1650 auth_scheme = StringToAuthScheme(server_auth_->scheme); | |
1651 if (auth_scheme == 0) | |
1652 return; | |
1653 | |
1654 rv = WinHttpSetCredentials(request_handle_, | |
1655 WINHTTP_AUTH_TARGET_SERVER, | |
1656 auth_scheme, | |
1657 server_auth_->username.c_str(), | |
1658 server_auth_->password.c_str(), | |
1659 NULL); | |
1660 } | |
1661 } | |
1662 | |
1663 void HttpTransactionWinHttp::OnProxyInfoAvailable(int result) { | |
1664 if (result != OK) { | |
1665 DLOG(WARNING) << "failed to get proxy info: " << result; | |
1666 proxy_info_.UseDirect(); | |
1667 } | |
1668 | |
1669 // Balances extra reference taken when proxy resolution was initiated. | |
1670 session_callback_->Release(); | |
1671 | |
1672 pac_request_ = NULL; | |
1673 | |
1674 // Since OnProxyInfoAvailable is always called asynchronously (via the | |
1675 // message loop), we need to trap any errors and pass them to the consumer | |
1676 // via their completion callback. | |
1677 | |
1678 int rv = DidResolveProxy(); | |
1679 if (rv == ERR_IO_PENDING) { | |
1680 session_callback_->AddRef(); // balanced when callback runs. | |
1681 } else { | |
1682 DoCallback(rv); | |
1683 } | |
1684 } | |
1685 | |
1686 std::string HttpTransactionWinHttp::GetRequestHeaders() const { | |
1687 std::string headers; | |
1688 | |
1689 if (!request_->user_agent.empty()) | |
1690 headers += "User-Agent: " + request_->user_agent + "\r\n"; | |
1691 | |
1692 // Our consumer should have made sure that this is a safe referrer. See for | |
1693 // instance WebCore::FrameLoader::HideReferrer. | |
1694 if (request_->referrer.is_valid()) | |
1695 headers += "Referer: " + request_->referrer.spec() + "\r\n"; | |
1696 | |
1697 // IE and Safari do this. Presumably it is to support sending a HEAD request | |
1698 // to an URL that only expects to be sent a POST or some other method that | |
1699 // normally would have a message body. | |
1700 if (request_->method == "HEAD") | |
1701 headers += "Content-Length: 0\r\n"; | |
1702 | |
1703 // Honor load flags that impact proxy caches. | |
1704 if (request_->load_flags & LOAD_BYPASS_CACHE) { | |
1705 headers += "Pragma: no-cache\r\nCache-Control: no-cache\r\n"; | |
1706 } else if (request_->load_flags & LOAD_VALIDATE_CACHE) { | |
1707 headers += "Cache-Control: max-age=0\r\n"; | |
1708 } | |
1709 | |
1710 // TODO(darin): Prune out duplicate headers? | |
1711 headers += request_->extra_headers; | |
1712 | |
1713 return headers; | |
1714 } | |
1715 | |
1716 // Retrieves the SSL server certificate associated with the transaction. | |
1717 // The caller is responsible for freeing the certificate. | |
1718 X509Certificate* HttpTransactionWinHttp::GetServerCertificate() const { | |
1719 DCHECK(is_https_); | |
1720 PCCERT_CONTEXT cert_context = NULL; | |
1721 DWORD length = sizeof(cert_context); | |
1722 if (!WinHttpQueryOption(request_handle_, | |
1723 WINHTTP_OPTION_SERVER_CERT_CONTEXT, | |
1724 &cert_context, | |
1725 &length)) { | |
1726 return NULL; | |
1727 } | |
1728 // cert_context may be NULL here even though WinHttpQueryOption succeeded. | |
1729 // For example, a proxy server may return a 404 error page to report the | |
1730 // DNS resolution failure of the server's hostname. | |
1731 if (!cert_context) | |
1732 return NULL; | |
1733 return X509Certificate::CreateFromHandle(cert_context); | |
1734 } | |
1735 | |
1736 // Retrieves the security strength, in bits, of the SSL cipher suite | |
1737 // associated with the transaction. | |
1738 int HttpTransactionWinHttp::GetSecurityBits() const { | |
1739 DCHECK(is_https_); | |
1740 DWORD key_bits = 0; | |
1741 DWORD length = sizeof(key_bits); | |
1742 if (!WinHttpQueryOption(request_handle_, | |
1743 WINHTTP_OPTION_SECURITY_KEY_BITNESS, | |
1744 &key_bits, | |
1745 &length)) { | |
1746 return -1; | |
1747 } | |
1748 return key_bits; | |
1749 } | |
1750 | |
1751 void HttpTransactionWinHttp::PopulateSSLInfo(DWORD secure_failure) { | |
1752 if (is_https_) { | |
1753 response_.ssl_info.cert = GetServerCertificate(); | |
1754 response_.ssl_info.security_bits = GetSecurityBits(); | |
1755 // If there is no cert (such as when the proxy server makes up a | |
1756 // 404 response to report a server name resolution error), don't set | |
1757 // the cert status. | |
1758 if (!response_.ssl_info.cert) | |
1759 return; | |
1760 response_.ssl_info.cert_status = | |
1761 MapSecureFailureToCertStatus(secure_failure); | |
1762 // WinHTTP does not always return a cert status once we ignored errors | |
1763 // for a cert. (Our experiments showed that WinHTTP reliably returns a | |
1764 // cert status only when there are unignored errors or when we resend a | |
1765 // request with the errors ignored.) So we have to remember what the | |
1766 // last status was for a cert. Note that if the cert status changes | |
1767 // from error to OK, we won't know that. If we have never stored our | |
1768 // status in the CertStatusCache (meaning no errors so far), then it is | |
1769 // OK (0). | |
1770 CertStatusCache* cert_status_cache = session_->cert_status_cache(); | |
1771 if (net::IsCertStatusError(response_.ssl_info.cert_status)) { | |
1772 cert_status_cache->SetCertStatus(*response_.ssl_info.cert, | |
1773 request_->url.host(), | |
1774 response_.ssl_info.cert_status); | |
1775 } else { | |
1776 response_.ssl_info.cert_status |= | |
1777 cert_status_cache->GetCertStatus(*response_.ssl_info.cert, | |
1778 request_->url.host()) & | |
1779 net::CERT_STATUS_ALL_ERRORS; | |
1780 } | |
1781 | |
1782 if (rev_checking_enabled_) | |
1783 response_.ssl_info.cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; | |
1784 } else { | |
1785 // If this is not https, we should not get a cert status. | |
1786 DCHECK(!secure_failure); | |
1787 } | |
1788 } | |
1789 | |
1790 void HttpTransactionWinHttp::HandleStatusCallback(DWORD status, | |
1791 DWORD_PTR result, | |
1792 DWORD error, | |
1793 DWORD secure_failure) { | |
1794 int rv; | |
1795 | |
1796 switch (status) { | |
1797 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: | |
1798 rv = DidReceiveError(error, secure_failure); | |
1799 break; | |
1800 case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: | |
1801 PopulateSSLInfo(secure_failure); | |
1802 rv = DidSendRequest(); | |
1803 break; | |
1804 case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: | |
1805 rv = DidWriteData(static_cast<DWORD>(result)); | |
1806 break; | |
1807 case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: | |
1808 rv = DidReceiveHeaders(); | |
1809 break; | |
1810 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: | |
1811 rv = DidReadData(static_cast<DWORD>(result)); | |
1812 break; | |
1813 default: | |
1814 NOTREACHED() << "unexpected status code"; | |
1815 rv = ERR_UNEXPECTED; | |
1816 break; | |
1817 } | |
1818 | |
1819 if (rv == ERR_IO_PENDING) { | |
1820 session_callback_->AddRef(); // balanced when callback runs. | |
1821 } else if (callback_) { | |
1822 DoCallback(rv); | |
1823 } | |
1824 } | |
1825 | |
1826 } // namespace net | |
1827 | |
OLD | NEW |