OLD | NEW |
| (Empty) |
1 // Copyright 2007-2010 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 // The implementation does not allow concurrent calls on the same object but | |
17 // it allows calling SimpleRequest::Cancel in order to stop an ongoing request. | |
18 // The transient state of the request is maintained by request_state_. | |
19 // The state is created by SimpleRequest::Send and it is destroyed by | |
20 // SimpleRequest::Close. The only concurrent access to the object state can | |
21 // happen during calling SimpleRequest::Cancel(), Pause() and Resume(). Cancel | |
22 // closes the connection and the request handles. This makes any of the WinHttp | |
23 // calls on these handles fail and SimpleRequest::Send return to the caller. | |
24 // Pause() also closes the handles but SimpleRequest automatically reopens | |
25 // them when Resume() is called. During resume stage, SimpleRequest sends a | |
26 // range request to continue download. During these actions, the caller is still | |
27 // blocked. | |
28 | |
29 #include "omaha/net/simple_request.h" | |
30 #include <atlconv.h> | |
31 #include <climits> | |
32 #include <memory> | |
33 #include <vector> | |
34 #include "omaha/base/const_addresses.h" | |
35 #include "omaha/base/constants.h" | |
36 #include "omaha/base/debug.h" | |
37 #include "omaha/base/error.h" | |
38 #include "omaha/base/logging.h" | |
39 #include "omaha/base/scoped_any.h" | |
40 #include "omaha/base/scope_guard.h" | |
41 #include "omaha/base/string.h" | |
42 #include "omaha/net/network_config.h" | |
43 #include "omaha/net/network_request.h" | |
44 #include "omaha/net/proxy_auth.h" | |
45 #include "omaha/net/winhttp_adapter.h" | |
46 | |
47 namespace omaha { | |
48 | |
49 SimpleRequest::SimpleRequest() | |
50 : request_buffer_(NULL), | |
51 request_buffer_length_(0), | |
52 proxy_auth_config_(NULL, CString()), | |
53 is_canceled_(false), | |
54 is_closed_(false), | |
55 session_handle_(NULL), | |
56 low_priority_(false), | |
57 callback_(NULL), | |
58 download_completed_(false), | |
59 pause_happened_(false) { | |
60 user_agent_.Format(_T("%s;winhttp"), NetworkConfig::GetUserAgent()); | |
61 | |
62 // Create a manual reset event to wait on during network transfer. | |
63 // The event is signaled by default meaning the network transferring is | |
64 // enabled. Pause() resets this event to stop network activity. Resume() | |
65 // does the opposite as Pause(). Close() and Cancel() also set the event. | |
66 // This unlocks the thread if it is in paused state and returns control | |
67 // to the caller. | |
68 reset(event_resume_, ::CreateEvent(NULL, true, true, NULL)); | |
69 ASSERT1(valid(event_resume_)); | |
70 } | |
71 | |
72 // TODO(omaha): we should attempt to cleanup the file only if we | |
73 // created it in the first place. | |
74 SimpleRequest::~SimpleRequest() { | |
75 Close(); | |
76 callback_ = NULL; | |
77 | |
78 // If download failed, try to clean up the target file. | |
79 if (!download_completed_ && !filename_.IsEmpty()) { | |
80 if (!::DeleteFile(filename_) && ::GetLastError() != ERROR_FILE_NOT_FOUND) { | |
81 NET_LOG(LW, (_T("[SimpleRequest][Failed to delete file: %s][0x%08x]."), | |
82 filename_.GetString(), HRESULTFromLastError())); | |
83 } | |
84 } | |
85 } | |
86 | |
87 void SimpleRequest::set_url(const CString& url) { | |
88 __mutexScope(lock_); | |
89 if (url_ != url) { | |
90 url_ = url; | |
91 CloseHandles(); | |
92 request_state_.reset(); | |
93 } | |
94 } | |
95 | |
96 void SimpleRequest::set_filename(const CString& filename) { | |
97 __mutexScope(lock_); | |
98 if (filename_ != filename) { | |
99 filename_ = filename; | |
100 CloseHandles(); | |
101 request_state_.reset(); | |
102 } | |
103 } | |
104 | |
105 HRESULT SimpleRequest::Close() { | |
106 NET_LOG(L3, (_T("[SimpleRequest::Close]"))); | |
107 | |
108 __mutexScope(lock_); | |
109 is_closed_ = true; | |
110 CloseHandles(); | |
111 request_state_.reset(); | |
112 winhttp_adapter_.reset(); | |
113 | |
114 // Resume the downloading thread if it is blocked. It is still fine if the | |
115 // event is set since the operation is like no-op in that case. | |
116 return Resume(); | |
117 } | |
118 | |
119 HRESULT SimpleRequest::Cancel() { | |
120 NET_LOG(L3, (_T("[SimpleRequest::Cancel]"))); | |
121 | |
122 __mutexScope(lock_); | |
123 is_canceled_ = true; | |
124 CloseHandles(); | |
125 | |
126 // Resume the downloading thread if it is blocked. It is still fine if the | |
127 // event is set since the operation is like no-op in that case. | |
128 return Resume(); | |
129 } | |
130 | |
131 void SimpleRequest::CloseHandles() { | |
132 if (winhttp_adapter_.get()) { | |
133 winhttp_adapter_->CloseHandles(); | |
134 } | |
135 } | |
136 | |
137 bool SimpleRequest::IsResumeNeeded() const { | |
138 __mutexScope(lock_); | |
139 if (!IsPauseSupported() || is_canceled_ || is_closed_) { | |
140 return false; | |
141 } | |
142 | |
143 return pause_happened_; | |
144 } | |
145 | |
146 bool SimpleRequest::IsPauseSupported() const { | |
147 __mutexScope(lock_); | |
148 return valid(event_resume_) && !IsPostRequest() && !filename_.IsEmpty(); | |
149 } | |
150 | |
151 HRESULT SimpleRequest::Pause() { | |
152 NET_LOG(L3, (_T("[SimpleRequest::Pause]"))); | |
153 | |
154 if (!IsPauseSupported()) { | |
155 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); | |
156 } | |
157 | |
158 __mutexScope(ready_to_pause_lock_); | |
159 __mutexScope(lock_); | |
160 | |
161 pause_happened_ = true; | |
162 CloseHandles(); | |
163 return ::ResetEvent(get(event_resume_)) ? S_OK : HRESULTFromLastError(); | |
164 } | |
165 | |
166 HRESULT SimpleRequest::Resume() { | |
167 NET_LOG(L3, (_T("[SimpleRequest::Resume]"))); | |
168 | |
169 __mutexScope(lock_); | |
170 HRESULT hr = S_OK; | |
171 if (IsPauseSupported()) { | |
172 hr = ::SetEvent(get(event_resume_)) ? S_OK : HRESULTFromLastError(); | |
173 } | |
174 return hr; | |
175 } | |
176 | |
177 void SimpleRequest::WaitForResumeEvent() { | |
178 if (!event_resume_) { | |
179 return; | |
180 } | |
181 | |
182 // Reset pause_happened_ state to indicate that pause has not yet happened | |
183 // during new resume stage. | |
184 __mutexBlock(lock_) { | |
185 pause_happened_ = false; | |
186 } | |
187 | |
188 VERIFY1(::WaitForSingleObject(get(event_resume_), INFINITE) != WAIT_FAILED); | |
189 } | |
190 | |
191 HRESULT SimpleRequest::Send() { | |
192 NET_LOG(L3, (_T("[SimpleRequest::Send][%s]"), url_)); | |
193 | |
194 ASSERT1(!url_.IsEmpty()); | |
195 if (!session_handle_) { | |
196 NET_LOG(LE, (_T("[SimpleRequest: session_handle_ is NULL]"))); | |
197 return OMAHA_NET_E_WINHTTP_NOT_AVAILABLE; | |
198 } | |
199 | |
200 HRESULT hr = S_OK; | |
201 | |
202 __mutexBlock(ready_to_pause_lock_) { | |
203 __mutexBlock(lock_) { | |
204 winhttp_adapter_.reset(new WinHttpAdapter()); | |
205 hr = winhttp_adapter_->Initialize(); | |
206 if (FAILED(hr)) { | |
207 return hr; | |
208 } | |
209 | |
210 if (!IsPauseSupported() || request_state_ == NULL) { | |
211 request_state_.reset(new TransientRequestState); | |
212 } else { | |
213 // Discard all previous download states except content_length and | |
214 // current_bytes for resume purpose. These two states will be validated | |
215 // against the previously (partially) downloaded file when reopens the | |
216 // target file. | |
217 scoped_ptr<TransientRequestState> request_state( | |
218 new TransientRequestState); | |
219 request_state->content_length = request_state_->content_length; | |
220 request_state->current_bytes = request_state_->current_bytes; | |
221 | |
222 request_state_.swap(request_state); | |
223 } | |
224 } | |
225 } | |
226 | |
227 for (bool first_time = true, cancelled = false; | |
228 !cancelled; | |
229 first_time = false) { | |
230 scoped_hfile file_handle; | |
231 | |
232 __mutexBlock(ready_to_pause_lock_) { | |
233 if (!first_time) { | |
234 if (IsResumeNeeded()) { | |
235 NET_LOG(L3, (_T("[SimpleRequest::Send paused.]"))); | |
236 WaitForResumeEvent(); | |
237 NET_LOG(L3, (_T("[SimpleRequest::Send resumed.]"))); | |
238 } else { | |
239 if (is_canceled_) { | |
240 hr = GOOPDATE_E_CANCELLED; | |
241 | |
242 // Once cancelled, we should set downloaded bytes to 0 | |
243 request_state_->current_bytes = 0; | |
244 } | |
245 | |
246 // Macro __mutexBlock has a hidden loop built-in and thus one break | |
247 // is not enough to exit the loop. Uses a flag instead. | |
248 cancelled = true; | |
249 } | |
250 } | |
251 | |
252 if (!cancelled) { | |
253 hr = PrepareRequest(address(file_handle)); | |
254 } | |
255 } | |
256 | |
257 if (SUCCEEDED(hr) && !cancelled) { | |
258 ASSERT1(request_state_->current_bytes <= request_state_->content_length); | |
259 | |
260 // Only requests data if there is more data to download. | |
261 if (request_state_->content_length == 0 || | |
262 request_state_->current_bytes < request_state_->content_length) { | |
263 hr = RequestData(get(file_handle)); | |
264 } | |
265 } | |
266 } | |
267 | |
268 NET_LOG(L3, | |
269 (_T("[SimpleRequest::Send][0x%08x][%d]"), hr, GetHttpStatusCode())); | |
270 return hr; | |
271 } | |
272 | |
273 HRESULT SimpleRequest::Connect() { | |
274 HRESULT hr = winhttp_adapter_->CrackUrl(url_, | |
275 ICU_DECODE, | |
276 &request_state_->scheme, | |
277 &request_state_->server, | |
278 &request_state_->port, | |
279 &request_state_->url_path, | |
280 NULL); | |
281 if (FAILED(hr)) { | |
282 return hr; | |
283 } | |
284 ASSERT1(!request_state_->scheme.CompareNoCase(kHttpProtoScheme) || | |
285 !request_state_->scheme.CompareNoCase(kHttpsProtoScheme)); | |
286 | |
287 hr = winhttp_adapter_->Connect(session_handle_, | |
288 request_state_->server, | |
289 request_state_->port); | |
290 if (FAILED(hr)) { | |
291 return hr; | |
292 } | |
293 | |
294 // TODO(omaha): figure out the accept types. | |
295 // figure out more flags. | |
296 request_state_->is_https = false; | |
297 DWORD flags = WINHTTP_FLAG_REFRESH; | |
298 if (request_state_->scheme == kHttpsProtoScheme) { | |
299 request_state_->is_https = true; | |
300 flags |= WINHTTP_FLAG_SECURE; | |
301 } | |
302 const TCHAR* verb = IsPostRequest() ? _T("POST") : _T("GET"); | |
303 hr = winhttp_adapter_->OpenRequest(verb, request_state_->url_path, | |
304 NULL, WINHTTP_NO_REFERER, | |
305 WINHTTP_DEFAULT_ACCEPT_TYPES, flags); | |
306 if (FAILED(hr)) { | |
307 return hr; | |
308 } | |
309 | |
310 // Disable redirects for POST requests. | |
311 if (IsPostRequest()) { | |
312 VERIFY1(SUCCEEDED( | |
313 winhttp_adapter_->SetRequestOptionInt(WINHTTP_OPTION_DISABLE_FEATURE, | |
314 WINHTTP_DISABLE_REDIRECTS))); | |
315 } | |
316 | |
317 CString additional_headers = additional_headers_; | |
318 | |
319 // If the target has been partially downloaded, send a range request to resume | |
320 // download, instead of starting from scratch again. | |
321 if (request_state_->current_bytes != 0 && | |
322 request_state_->current_bytes != request_state_->content_length) { | |
323 ASSERT1(request_state_->current_bytes < request_state_->content_length); | |
324 additional_headers.AppendFormat(_T("Range: bytes=%d-\r\n"), | |
325 request_state_->current_bytes); | |
326 } | |
327 if (!additional_headers.IsEmpty()) { | |
328 uint32 header_flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE; | |
329 hr = winhttp_adapter_->AddRequestHeaders(additional_headers, | |
330 -1, | |
331 header_flags); | |
332 if (FAILED(hr)) { | |
333 return hr; | |
334 } | |
335 } | |
336 | |
337 // If the WPAD detection fails, allow the request to go direct connection. | |
338 SetProxyInformation(); | |
339 | |
340 return S_OK; | |
341 } | |
342 | |
343 HRESULT SimpleRequest::OpenDestinationFile(HANDLE* file_handle) { | |
344 ASSERT1(!filename_.IsEmpty()); | |
345 ASSERT1(file_handle); | |
346 | |
347 DWORD create_disposition = request_state_->content_length == 0 ? | |
348 CREATE_ALWAYS : OPEN_ALWAYS; | |
349 | |
350 scoped_hfile file(::CreateFile(filename_, GENERIC_WRITE, 0, NULL, | |
351 create_disposition, FILE_ATTRIBUTE_NORMAL, | |
352 NULL)); | |
353 | |
354 if (!file) { | |
355 return HRESULTFromLastError(); | |
356 } | |
357 | |
358 if (request_state_->content_length != 0) { | |
359 DWORD raw_file_size = ::GetFileSize(get(file), NULL); | |
360 if (INVALID_FILE_SIZE == raw_file_size || raw_file_size > INT_MAX) { | |
361 return E_FAIL; | |
362 } | |
363 int file_size = static_cast<int>(raw_file_size); | |
364 | |
365 // Local file size should not be greater than remote file size and file | |
366 // size must match the number of bytes we previously downloaded. If not, | |
367 // reset the local file. | |
368 bool need_reset_file = file_size > request_state_->content_length; | |
369 need_reset_file |= file_size != request_state_->current_bytes; | |
370 | |
371 if (need_reset_file) { | |
372 // Need to download from byte 0. Reopen the file with truncation. | |
373 request_state_->current_bytes = 0; | |
374 reset(file, ::CreateFile(filename_, GENERIC_WRITE, 0, NULL, | |
375 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); | |
376 | |
377 if (!file) { | |
378 return HRESULTFromLastError(); | |
379 } | |
380 } else { | |
381 LARGE_INTEGER start_pos; | |
382 start_pos.LowPart = static_cast<DWORD>(request_state_->current_bytes); | |
383 start_pos.HighPart = 0; | |
384 if (!::SetFilePointerEx(get(file), start_pos, NULL, FILE_BEGIN)) { | |
385 return HRESULTFromLastError(); | |
386 } | |
387 } | |
388 } else { | |
389 // Always start from byte 0 if we don't know remote file size. | |
390 request_state_->current_bytes = 0; | |
391 } | |
392 | |
393 *file_handle = release(file); | |
394 return S_OK; | |
395 } | |
396 | |
397 HRESULT SimpleRequest::SendRequest() { | |
398 int proxy_retry_count = 0; | |
399 int max_proxy_retries = 1; | |
400 CString username; | |
401 CString password; | |
402 HRESULT hr = S_OK; | |
403 | |
404 bool done = false; | |
405 while (!done) { | |
406 uint32& request_scheme = request_state_->proxy_authentication_scheme; | |
407 if (request_scheme) { | |
408 NET_LOG(L3, (_T("[SimpleRequest::SendRequest][auth_scheme][%d]"), | |
409 request_scheme)); | |
410 winhttp_adapter_->SetCredentials(WINHTTP_AUTH_TARGET_PROXY, | |
411 request_scheme, | |
412 username, password); | |
413 | |
414 CString headers; | |
415 headers.Format(_T("%s: %d\r\n"), | |
416 kHeaderXProxyRetryCount, proxy_retry_count); | |
417 if (!username.IsEmpty()) { | |
418 headers.AppendFormat(_T("%s: 1\r\n"), kHeaderXProxyManualAuth); | |
419 } | |
420 uint32 flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE; | |
421 VERIFY1(SUCCEEDED(winhttp_adapter_->AddRequestHeaders(headers, | |
422 -1, | |
423 flags))); | |
424 } | |
425 | |
426 size_t bytes_to_send = request_buffer_length_; | |
427 hr = winhttp_adapter_->SendRequest(NULL, | |
428 0, | |
429 request_buffer_, | |
430 bytes_to_send, | |
431 bytes_to_send); | |
432 if (FAILED(hr)) { | |
433 return hr; | |
434 } | |
435 NET_LOG(L3, | |
436 (_T("[SimpleRequest::SendRequest][request sent][server: %s][IP: %s]"), | |
437 winhttp_adapter_->server_name(), | |
438 winhttp_adapter_->server_ip())); | |
439 | |
440 hr = winhttp_adapter_->ReceiveResponse(); | |
441 #if DEBUG | |
442 LogResponseHeaders(); | |
443 #endif | |
444 if (hr == ERROR_WINHTTP_RESEND_REQUEST) { | |
445 // Resend the request if needed, likely because the authentication | |
446 // scheme requires many transactions on the same handle. | |
447 continue; | |
448 } else if (FAILED(hr)) { | |
449 return hr; | |
450 } | |
451 | |
452 hr = winhttp_adapter_->QueryRequestHeadersInt( | |
453 WINHTTP_QUERY_STATUS_CODE, | |
454 NULL, | |
455 &request_state_->http_status_code, | |
456 NULL); | |
457 if (FAILED(hr)) { | |
458 return hr; | |
459 } | |
460 if (request_state_->http_status_code < HTTP_STATUS_FIRST || | |
461 request_state_->http_status_code > HTTP_STATUS_LAST) { | |
462 return E_FAIL; | |
463 } | |
464 | |
465 NetworkConfigManager& network_manager = NetworkConfigManager::Instance(); | |
466 switch (request_state_->http_status_code) { | |
467 case HTTP_STATUS_DENIED: | |
468 // 401 responses are not supported. Omaha does not have to authenticate | |
469 // to our backend. | |
470 done = true; | |
471 break; | |
472 | |
473 case HTTP_STATUS_PROXY_AUTH_REQ: { | |
474 NET_LOG(L2, (_T("[http proxy requires authentication]"))); | |
475 ++proxy_retry_count; | |
476 if (proxy_retry_count > max_proxy_retries) { | |
477 // If we get multiple 407s in a row then we are done. It does not make | |
478 // sense to retry further. | |
479 done = true; | |
480 break; | |
481 } | |
482 if (!request_scheme) { | |
483 uint32 supported_schemes(0), first_scheme(0), auth_target(0); | |
484 hr = winhttp_adapter_->QueryAuthSchemes(&supported_schemes, | |
485 &first_scheme, | |
486 &auth_target); | |
487 if (FAILED(hr)) { | |
488 return hr; | |
489 } | |
490 ASSERT1(auth_target == WINHTTP_AUTH_TARGET_PROXY); | |
491 request_scheme = ChooseProxyAuthScheme(supported_schemes); | |
492 ASSERT1(request_scheme); | |
493 NET_LOG(L3, (_T("[SimpleRequest::SendRequest][Auth scheme][%d]"), | |
494 request_scheme)); | |
495 if (request_scheme == WINHTTP_AUTH_SCHEME_NEGOTIATE || | |
496 request_scheme == WINHTTP_AUTH_SCHEME_NTLM) { | |
497 // Increases the retry count. Tries to do an autologon at first, and | |
498 // if that fails, will call GetProxyCredentials below. | |
499 ++max_proxy_retries; | |
500 break; | |
501 } | |
502 } | |
503 | |
504 uint32 auth_scheme = UNKNOWN_AUTH_SCHEME; | |
505 // May prompt the user for credentials, or get cached credentials. | |
506 NetworkConfig* network_config = NULL; | |
507 hr = network_manager.GetUserNetworkConfig(&network_config); | |
508 if (FAILED(hr)) { | |
509 return hr; | |
510 } | |
511 if (!network_config->GetProxyCredentials(true, | |
512 false, | |
513 request_state_->proxy, | |
514 proxy_auth_config_, | |
515 request_state_->is_https, | |
516 &username, | |
517 &password, | |
518 &auth_scheme)) { | |
519 NET_LOG(LE, | |
520 (_T("[SimpleRequest::SendRequest][GetProxyCreds failed]"))); | |
521 done = true; | |
522 break; | |
523 } | |
524 if (auth_scheme != UNKNOWN_AUTH_SCHEME) { | |
525 // Uses the known scheme that was successful previously. | |
526 request_scheme = auth_scheme; | |
527 } | |
528 break; | |
529 } | |
530 | |
531 default: | |
532 // We got some kind of response. If we have a valid username, we | |
533 // record the auth scheme with the NetworkConfig, so it can be cached | |
534 // for future use within this process for current user.. | |
535 if (!username.IsEmpty()) { | |
536 NetworkConfig* network_config = NULL; | |
537 hr = network_manager.GetUserNetworkConfig(&network_config); | |
538 if (SUCCEEDED(hr)) { | |
539 VERIFY1(SUCCEEDED(network_config->SetProxyAuthScheme( | |
540 request_state_->proxy, request_state_->is_https, | |
541 request_scheme))); | |
542 } | |
543 } | |
544 done = true; | |
545 break; | |
546 } | |
547 } | |
548 | |
549 return hr; | |
550 } | |
551 | |
552 HRESULT SimpleRequest::ReceiveData(HANDLE file_handle) { | |
553 ASSERT1(file_handle != INVALID_HANDLE_VALUE || filename_.IsEmpty()); | |
554 | |
555 HRESULT hr = S_OK; | |
556 | |
557 // In the case of a "204 No Content" response, WinHttp blocks when | |
558 // querying or reading the available data. According to the RFC, | |
559 // the 204 response must not include a message-body, and thus is always | |
560 // terminated by the first empty line after the header fields. | |
561 // It appears WinHttp does not internally handles the 204 response. If this, | |
562 // condition is not handled here explicitly, WinHttp will timeout when | |
563 // waiting for the data instead of returning right away. | |
564 if (request_state_->http_status_code == HTTP_STATUS_NO_CONTENT) { | |
565 return S_OK; | |
566 } | |
567 | |
568 int content_length = 0; | |
569 winhttp_adapter_->QueryRequestHeadersInt(WINHTTP_QUERY_CONTENT_LENGTH, | |
570 WINHTTP_HEADER_NAME_BY_INDEX, | |
571 &content_length, | |
572 WINHTTP_NO_HEADER_INDEX); | |
573 if (request_state_->content_length == 0) { | |
574 request_state_->content_length = content_length; | |
575 request_state_->current_bytes = 0; | |
576 } | |
577 | |
578 const bool is_http_success = | |
579 request_state_->http_status_code == HTTP_STATUS_OK || | |
580 request_state_->http_status_code == HTTP_STATUS_PARTIAL_CONTENT; | |
581 | |
582 std::vector<uint8> buffer; | |
583 do { | |
584 DWORD bytes_available(0); | |
585 winhttp_adapter_->QueryDataAvailable(&bytes_available); | |
586 buffer.resize(1 + bytes_available); | |
587 hr = winhttp_adapter_->ReadData(&buffer.front(), | |
588 buffer.size(), | |
589 &bytes_available); | |
590 if (FAILED(hr)) { | |
591 return hr; | |
592 } | |
593 | |
594 buffer.resize(bytes_available); | |
595 if (!buffer.empty()) { | |
596 if (!filename_.IsEmpty()) { | |
597 DWORD num_bytes(0); | |
598 if (!::WriteFile(file_handle, | |
599 reinterpret_cast<const char*>(&buffer.front()), | |
600 buffer.size(), &num_bytes, NULL)) { | |
601 return HRESULTFromLastError(); | |
602 } | |
603 ASSERT1(num_bytes == buffer.size()); | |
604 } else { | |
605 request_state_->response.insert(request_state_->response.end(), | |
606 buffer.begin(), | |
607 buffer.end()); | |
608 } | |
609 } | |
610 | |
611 // Update current_bytes after those bytes are serialized in case we | |
612 // pause before current_bytes is updated, we can throw away the last | |
613 // batch of bytes received and resume. | |
614 request_state_->current_bytes += bytes_available; | |
615 if (request_state_->content_length) { | |
616 ASSERT1(request_state_->current_bytes <= request_state_->content_length); | |
617 } | |
618 | |
619 // The callback is called only for 200 or 206 http codes. | |
620 if (callback_ && request_state_->content_length && is_http_success) { | |
621 callback_->OnProgress(request_state_->current_bytes, | |
622 request_state_->content_length, | |
623 WINHTTP_CALLBACK_STATUS_READ_COMPLETE, | |
624 NULL); | |
625 } | |
626 } while (!buffer.empty()); | |
627 | |
628 NET_LOG(L3, (_T("[bytes downloaded %d]"), request_state_->current_bytes)); | |
629 if (file_handle != INVALID_HANDLE_VALUE) { | |
630 // All bytes must be written to the file in the file download case. | |
631 ASSERT1(::SetFilePointer(file_handle, 0, NULL, FILE_CURRENT) == | |
632 static_cast<DWORD>(request_state_->current_bytes)); | |
633 } | |
634 | |
635 download_completed_ = true; | |
636 return hr; | |
637 } | |
638 | |
639 HRESULT SimpleRequest::PrepareRequest(HANDLE* file_handle) { | |
640 // Read the remaining bytes of the body. If we have a file to save the | |
641 // response into, create the file. | |
642 HRESULT hr = S_OK; | |
643 if (!filename_.IsEmpty()) { | |
644 hr = OpenDestinationFile(file_handle); | |
645 if (FAILED(hr)) { | |
646 return hr; | |
647 } | |
648 } else { | |
649 // Always restarts if downloading to memory. | |
650 request_state_->current_bytes = 0; | |
651 request_state_->response.clear(); | |
652 } | |
653 | |
654 return Connect(); | |
655 } | |
656 | |
657 HRESULT SimpleRequest::RequestData(HANDLE file_handle) { | |
658 HRESULT hr = SendRequest(); | |
659 if (FAILED(hr)) { | |
660 return hr; | |
661 } | |
662 | |
663 return ReceiveData(file_handle); | |
664 } | |
665 | |
666 std::vector<uint8> SimpleRequest::GetResponse() const { | |
667 return request_state_.get() ? request_state_->response : | |
668 std::vector<uint8>(); | |
669 } | |
670 | |
671 HRESULT SimpleRequest::QueryHeadersString(uint32 info_level, | |
672 const TCHAR* name, | |
673 CString* value) const { | |
674 // Name can be null when the info_level specifies the header to query. | |
675 if (winhttp_adapter_.get()) { | |
676 return winhttp_adapter_->QueryRequestHeadersString(info_level, | |
677 name, | |
678 value, | |
679 WINHTTP_NO_HEADER_INDEX); | |
680 } else { | |
681 return E_UNEXPECTED; | |
682 } | |
683 } | |
684 | |
685 CString SimpleRequest::GetResponseHeaders() const { | |
686 CString response_headers; | |
687 if (winhttp_adapter_.get()) { | |
688 CString response_headers; | |
689 if (SUCCEEDED(winhttp_adapter_->QueryRequestHeadersString( | |
690 WINHTTP_QUERY_RAW_HEADERS_CRLF, | |
691 WINHTTP_HEADER_NAME_BY_INDEX, | |
692 &response_headers, | |
693 WINHTTP_NO_HEADER_INDEX))) { | |
694 return response_headers; | |
695 } | |
696 } | |
697 return CString(); | |
698 } | |
699 | |
700 uint32 SimpleRequest::ChooseProxyAuthScheme(uint32 supported_schemes) { | |
701 // It is the server's responsibility only to accept | |
702 // authentication schemes that provide a sufficient level | |
703 // of security to protect the server's resources. | |
704 // | |
705 // The client is also obligated only to use an authentication | |
706 // scheme that adequately protects its username and password. | |
707 // | |
708 // TODO(omaha): remove Basic authentication because Basic authentication | |
709 // exposes the client's username and password to anyone monitoring | |
710 // the connection. This option is here for Fiddler testing purposes. | |
711 | |
712 uint32 auth_schemes[] = { | |
713 WINHTTP_AUTH_SCHEME_NEGOTIATE, | |
714 WINHTTP_AUTH_SCHEME_NTLM, | |
715 WINHTTP_AUTH_SCHEME_DIGEST, | |
716 WINHTTP_AUTH_SCHEME_BASIC, | |
717 }; | |
718 for (int i = 0; i < arraysize(auth_schemes); ++i) { | |
719 if (supported_schemes & auth_schemes[i]) { | |
720 return auth_schemes[i]; | |
721 } | |
722 } | |
723 | |
724 return 0; | |
725 } | |
726 | |
727 void SimpleRequest::SetProxyInformation() { | |
728 bool uses_proxy = false; | |
729 CString proxy, proxy_bypass; | |
730 int access_type = NetworkConfig::GetAccessType(proxy_config_); | |
731 if (access_type == WINHTTP_ACCESS_TYPE_AUTO_DETECT) { | |
732 HttpClient::ProxyInfo proxy_info = {0}; | |
733 NetworkConfig* network_config = NULL; | |
734 NetworkConfigManager& network_manager = NetworkConfigManager::Instance(); | |
735 HRESULT hr = network_manager.GetUserNetworkConfig(&network_config); | |
736 if (SUCCEEDED(hr)) { | |
737 hr = network_config->GetProxyForUrl( | |
738 url_, | |
739 proxy_config_.auto_config_url, | |
740 &proxy_info); | |
741 if (SUCCEEDED(hr)) { | |
742 // The result of proxy auto-detection could be that either a proxy is | |
743 // found, or direct connection is allowed for the specified url. | |
744 ASSERT(proxy_info.access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY || | |
745 proxy_info.access_type == WINHTTP_ACCESS_TYPE_NO_PROXY, | |
746 (_T("[Unexpected access_type][%d]"), proxy_info.access_type)); | |
747 | |
748 uses_proxy = proxy_info.access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY; | |
749 | |
750 proxy = proxy_info.proxy; | |
751 proxy_bypass = proxy_info.proxy_bypass; | |
752 | |
753 ::GlobalFree(const_cast<wchar_t*>(proxy_info.proxy)); | |
754 ::GlobalFree(const_cast<wchar_t*>(proxy_info.proxy_bypass)); | |
755 } else { | |
756 ASSERT1(!uses_proxy); | |
757 NET_LOG(LW, (_T("[GetProxyForUrl failed][0x%08x]"), hr)); | |
758 } | |
759 } else { | |
760 NET_LOG(LW, (_T("[GetUserNetworkConfig failed][0x%08x]"), hr)); | |
761 } | |
762 } else if (access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY) { | |
763 uses_proxy = true; | |
764 proxy = proxy_config_.proxy; | |
765 proxy_bypass = proxy_config_.proxy_bypass; | |
766 } | |
767 | |
768 // If a proxy is going to be used, modify the state of the object and | |
769 // set the proxy information on the request handle. | |
770 if (uses_proxy) { | |
771 ASSERT1(!proxy.IsEmpty()); | |
772 | |
773 request_state_->proxy = proxy; | |
774 request_state_->proxy_bypass = proxy_bypass; | |
775 | |
776 HttpClient::ProxyInfo proxy_info = {0}; | |
777 proxy_info.access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY; | |
778 proxy_info.proxy = request_state_->proxy; | |
779 proxy_info.proxy_bypass = request_state_->proxy_bypass; | |
780 | |
781 NET_LOG(L3, (_T("[using proxy %s]"), proxy_info.proxy)); | |
782 VERIFY1(SUCCEEDED(winhttp_adapter_->SetRequestOption(WINHTTP_OPTION_PROXY, | |
783 &proxy_info, | |
784 sizeof(proxy_info)))); | |
785 } | |
786 } | |
787 | |
788 void SimpleRequest::LogResponseHeaders() { | |
789 CString response_headers; | |
790 winhttp_adapter_->QueryRequestHeadersString(WINHTTP_QUERY_RAW_HEADERS_CRLF, | |
791 WINHTTP_HEADER_NAME_BY_INDEX, | |
792 &response_headers, | |
793 WINHTTP_NO_HEADER_INDEX); | |
794 NET_LOG(L3, (_T("[response headers...]\r\n%s"), response_headers)); | |
795 } | |
796 | |
797 } // namespace omaha | |
798 | |
OLD | NEW |