| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome_frame/urlmon_url_request.h" | 5 #include "chrome_frame/urlmon_url_request.h" |
| 6 | 6 |
| 7 #include <wininet.h> | 7 #include <wininet.h> |
| 8 | 8 |
| 9 #include "base/scoped_ptr.h" | 9 #include "base/scoped_ptr.h" |
| 10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
| 13 #include "chrome_frame/chrome_frame_activex_base.h" | 13 #include "chrome_frame/chrome_frame_activex_base.h" |
| 14 #include "chrome_frame/html_utils.h" | 14 #include "chrome_frame/html_utils.h" |
| 15 #include "chrome_frame/urlmon_upload_data_stream.h" | 15 #include "chrome_frame/urlmon_upload_data_stream.h" |
| 16 #include "chrome_frame/utils.h" | 16 #include "chrome_frame/utils.h" |
| 17 #include "net/http/http_util.h" | 17 #include "net/http/http_util.h" |
| 18 #include "net/http/http_response_headers.h" | 18 #include "net/http/http_response_headers.h" |
| 19 | 19 |
| 20 static const LARGE_INTEGER kZero = {0}; | 20 static const LARGE_INTEGER kZero = {0}; |
| 21 static const ULARGE_INTEGER kUnsignedZero = {0}; | 21 static const ULARGE_INTEGER kUnsignedZero = {0}; |
| 22 int UrlmonUrlRequest::instance_count_ = 0; | 22 int UrlmonUrlRequest::instance_count_ = 0; |
| 23 | 23 |
| 24 UrlmonUrlRequest::UrlmonUrlRequest() | 24 UrlmonUrlRequest::UrlmonUrlRequest() |
| 25 : pending_read_size_(0), | 25 : pending_read_size_(0), |
| 26 status_(URLRequestStatus::FAILED, net::ERR_FAILED), | 26 status_(URLRequestStatus::FAILED, net::ERR_FAILED), |
| 27 thread_(PlatformThread::CurrentId()), | 27 thread_(PlatformThread::CurrentId()), |
| 28 redirect_status_(0), | 28 redirect_status_(0), |
| 29 parent_window_(NULL), | 29 parent_window_(NULL), |
| 30 worker_thread_(NULL) { | 30 worker_thread_(NULL), |
| 31 ignore_redirect_stop_binding_error_(false) { |
| 31 DLOG(INFO) << StringPrintf("Created request. Obj: %X", this) | 32 DLOG(INFO) << StringPrintf("Created request. Obj: %X", this) |
| 32 << " Count: " << ++instance_count_; | 33 << " Count: " << ++instance_count_; |
| 33 } | 34 } |
| 34 | 35 |
| 35 UrlmonUrlRequest::~UrlmonUrlRequest() { | 36 UrlmonUrlRequest::~UrlmonUrlRequest() { |
| 36 DLOG(INFO) << StringPrintf("Deleted request. Obj: %X", this) | 37 DLOG(INFO) << StringPrintf("Deleted request. Obj: %X", this) |
| 37 << " Count: " << --instance_count_; | 38 << " Count: " << --instance_count_; |
| 38 } | 39 } |
| 39 | 40 |
| 40 bool UrlmonUrlRequest::Start() { | 41 bool UrlmonUrlRequest::Start() { |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 170 *priority = THREAD_PRIORITY_NORMAL; | 171 *priority = THREAD_PRIORITY_NORMAL; |
| 171 return S_OK; | 172 return S_OK; |
| 172 } | 173 } |
| 173 | 174 |
| 174 STDMETHODIMP UrlmonUrlRequest::OnLowResource(DWORD reserved) { | 175 STDMETHODIMP UrlmonUrlRequest::OnLowResource(DWORD reserved) { |
| 175 return S_OK; | 176 return S_OK; |
| 176 } | 177 } |
| 177 | 178 |
| 178 STDMETHODIMP UrlmonUrlRequest::OnProgress(ULONG progress, ULONG max_progress, | 179 STDMETHODIMP UrlmonUrlRequest::OnProgress(ULONG progress, ULONG max_progress, |
| 179 ULONG status_code, LPCWSTR status_text) { | 180 ULONG status_code, LPCWSTR status_text) { |
| 181 static const int kDefaultHttpRedirectCode = 302; |
| 182 |
| 180 switch (status_code) { | 183 switch (status_code) { |
| 181 case BINDSTATUS_REDIRECTING: | 184 case BINDSTATUS_REDIRECTING: { |
| 185 // Fetch the redirect status as they aren't all equal (307 in particular |
| 186 // retains the HTTP request verb). |
| 187 // We assume that valid redirect codes are 301, 302, 303 and 307. If we |
| 188 // receive anything else we would abort the request which would |
| 189 // eventually result in the request getting cancelled in Chrome. |
| 190 int redirect_status = GetHttpResponseStatus(); |
| 182 DCHECK(status_text != NULL); | 191 DCHECK(status_text != NULL); |
| 183 DLOG(INFO) << "URL: " << url() << " redirected to " | 192 DLOG(INFO) << "URL: " << url() << " redirected to " |
| 184 << status_text; | 193 << status_text; |
| 185 redirect_url_ = status_text; | 194 redirect_url_ = status_text; |
| 186 // Fetch the redirect status as they aren't all equal (307 in particular | 195 redirect_status_ = |
| 187 // retains the HTTP request verb). | 196 redirect_status > 0 ? redirect_status : kDefaultHttpRedirectCode; |
| 188 redirect_status_ = GetHttpResponseStatus(); | 197 // Chrome should decide whether a redirect has to be followed. To achieve |
| 189 // NOTE: Even though RFC 2616 says to preserve the request method when | 198 // this we send over a fake response to Chrome and abort the redirect. |
| 190 // following a 302 redirect, normal browsers don't do that. Instead they | 199 std::string headers = GetHttpHeaders(); |
| 191 // all convert a POST into a GET in response to a 302 and so shall we. | 200 OnResponse(0, UTF8ToWide(headers).c_str(), NULL, NULL); |
| 192 // For 307 redirects, browsers preserve the method. The RFC says to | 201 ignore_redirect_stop_binding_error_ = true; |
| 193 // prompt the user to confirm the generation of a new POST request, but | 202 DCHECK(binding_ != NULL); |
| 194 // IE omits this prompt and so shall we. | 203 binding_->Abort(); |
| 195 if (redirect_status_ != 307 && | 204 binding_ = NULL; |
| 196 LowerCaseEqualsASCII(method(), "post")) { | 205 return E_ABORT; |
| 197 set_method("get"); | 206 } |
| 198 ClearPostData(); | |
| 199 } | |
| 200 break; | |
| 201 | 207 |
| 202 default: | 208 default: |
| 203 DLOG(INFO) << " Obj: " << std::hex << this << " OnProgress(" << url() | 209 DLOG(INFO) << " Obj: " << std::hex << this << " OnProgress(" << url() |
| 204 << StringPrintf(L") code: %i status: %ls", status_code, status_text); | 210 << StringPrintf(L") code: %i status: %ls", status_code, status_text); |
| 205 break; | 211 break; |
| 206 } | 212 } |
| 207 | 213 |
| 208 return S_OK; | 214 return S_OK; |
| 209 } | 215 } |
| 210 | 216 |
| 211 STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) { | 217 STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) { |
| 212 DCHECK(worker_thread_ != NULL); | 218 DCHECK(worker_thread_ != NULL); |
| 213 DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id()); | 219 DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id()); |
| 214 | 220 |
| 215 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << | 221 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << |
| 216 " - Request stopped, Result: " << std::hex << result << | 222 " - Request stopped, Result: " << std::hex << result << |
| 217 " Status: " << status_.status(); | 223 " Status: " << status_.status(); |
| 224 |
| 218 if (FAILED(result)) { | 225 if (FAILED(result)) { |
| 219 status_.set_status(URLRequestStatus::FAILED); | 226 status_.set_status(URLRequestStatus::FAILED); |
| 220 status_.set_os_error(HresultToNetError(result)); | 227 status_.set_os_error(HresultToNetError(result)); |
| 221 EndRequest(); | 228 EndRequest(); |
| 222 } else { | 229 } else { |
| 223 status_.set_status(URLRequestStatus::SUCCESS); | 230 status_.set_status(URLRequestStatus::SUCCESS); |
| 224 status_.set_os_error(0); | 231 status_.set_os_error(0); |
| 225 } | 232 } |
| 226 | 233 |
| 227 DLOG(INFO) << "OnStopBinding received for request id: " << id(); | 234 DLOG(INFO) << "OnStopBinding received for request id: " << id(); |
| (...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 600 } | 607 } |
| 601 | 608 |
| 602 DLOG_IF(ERROR, FAILED(hr)) | 609 DLOG_IF(ERROR, FAILED(hr)) |
| 603 << StringPrintf(L"StartAsyncDownload failed: 0x%08X", hr); | 610 << StringPrintf(L"StartAsyncDownload failed: 0x%08X", hr); |
| 604 | 611 |
| 605 return hr; | 612 return hr; |
| 606 } | 613 } |
| 607 | 614 |
| 608 void UrlmonUrlRequest::EndRequest() { | 615 void UrlmonUrlRequest::EndRequest() { |
| 609 DLOG(INFO) << __FUNCTION__; | 616 DLOG(INFO) << __FUNCTION__; |
| 610 // Special case. If the last request was a redirect and the current OS | 617 |
| 611 // error value is E_ACCESSDENIED, that means an unsafe redirect was attempted. | 618 // In case of a redirect notification we prevent urlmon from following the |
| 612 // In that case, correct the OS error value to be the more specific | 619 // redirect and rely on Chrome, in which case AutomationMsg_RequestEnd |
| 613 // ERR_UNSAFE_REDIRECT error value. | 620 // IPC will be sent over by Chrome to end this request. |
| 614 if (!status_.is_success() && status_.os_error() == net::ERR_ACCESS_DENIED) { | 621 if (!ignore_redirect_stop_binding_error_) { |
| 615 int status = GetHttpResponseStatus(); | 622 // Special case. If the last request was a redirect and the current OS |
| 616 if (status >= 300 && status < 400) { | 623 // error value is E_ACCESSDENIED, that means an unsafe redirect was |
| 617 redirect_status_ = status; // store the latest redirect status value. | 624 // attempted. In that case, correct the OS error value to be the more |
| 618 status_.set_os_error(net::ERR_UNSAFE_REDIRECT); | 625 // specific ERR_UNSAFE_REDIRECT error value. |
| 626 if (!status_.is_success() && status_.os_error() == net::ERR_ACCESS_DENIED) { |
| 627 int status = GetHttpResponseStatus(); |
| 628 if (status >= 300 && status < 400) { |
| 629 redirect_status_ = status; // store the latest redirect status value. |
| 630 status_.set_os_error(net::ERR_UNSAFE_REDIRECT); |
| 631 } |
| 619 } | 632 } |
| 633 OnResponseEnd(status_); |
| 634 } else { |
| 635 ignore_redirect_stop_binding_error_ = false; |
| 620 } | 636 } |
| 621 | 637 |
| 622 OnResponseEnd(status_); | |
| 623 | |
| 624 // Remove the request mapping and release the outstanding reference to us in | 638 // Remove the request mapping and release the outstanding reference to us in |
| 625 // the context of the UI thread. | 639 // the context of the UI thread. |
| 626 PostTask(FROM_HERE, | 640 PostTask(FROM_HERE, |
| 627 NewRunnableMethod(this, &UrlmonUrlRequest::EndRequestInternal)); | 641 NewRunnableMethod(this, &UrlmonUrlRequest::EndRequestInternal)); |
| 628 } | 642 } |
| 629 | 643 |
| 630 void UrlmonUrlRequest::EndRequestInternal() { | 644 void UrlmonUrlRequest::EndRequestInternal() { |
| 631 // The request object could have been removed from the map in the | 645 // The request object could have been removed from the map in the |
| 632 // OnRequestEnd callback which executes on receiving the | 646 // OnRequestEnd callback which executes on receiving the |
| 633 // AutomationMsg_RequestEnd IPC from Chrome. | 647 // AutomationMsg_RequestEnd IPC from Chrome. |
| 634 request_handler()->RemoveRequest(this); | 648 request_handler()->RemoveRequest(this); |
| 635 // The current instance could get destroyed in the context of DestroyWindow. | 649 // The current instance could get destroyed in the context of DestroyWindow. |
| 636 // We should not access the object after this. | 650 // We should not access the object after this. |
| 637 DestroyWindow(); | 651 DestroyWindow(); |
| 638 } | 652 } |
| 639 | 653 |
| 640 int UrlmonUrlRequest::GetHttpResponseStatus() const { | 654 int UrlmonUrlRequest::GetHttpResponseStatus() const { |
| 641 if (binding_ == NULL) { | 655 if (binding_ == NULL) { |
| 642 DLOG(WARNING) << "GetHttpResponseStatus - no binding_"; | 656 DLOG(WARNING) << "GetHttpResponseStatus - no binding_"; |
| 643 return 0; | 657 return 0; |
| 644 } | 658 } |
| 645 | 659 |
| 646 int http_status = 0; | 660 int http_status = 0; |
| 647 | 661 |
| 648 ScopedComPtr<IWinInetHttpInfo> info; | 662 ScopedComPtr<IWinInetHttpInfo> info; |
| 649 if (SUCCEEDED(info.QueryFrom(binding_))) { | 663 if (SUCCEEDED(info.QueryFrom(binding_))) { |
| 650 char status[10] = {0}; | 664 char status[10] = {0}; |
| 651 DWORD buf_size = sizeof(status); | 665 DWORD buf_size = sizeof(status); |
| 666 DWORD flags = 0; |
| 667 DWORD reserved = 0; |
| 652 if (SUCCEEDED(info->QueryInfo(HTTP_QUERY_STATUS_CODE, status, &buf_size, | 668 if (SUCCEEDED(info->QueryInfo(HTTP_QUERY_STATUS_CODE, status, &buf_size, |
| 653 0, NULL))) { | 669 &flags, &reserved))) { |
| 654 http_status = StringToInt(status); | 670 http_status = StringToInt(status); |
| 655 } else { | 671 } else { |
| 656 NOTREACHED() << "Failed to get HTTP status"; | 672 NOTREACHED() << "Failed to get HTTP status"; |
| 657 } | 673 } |
| 658 } else { | 674 } else { |
| 659 NOTREACHED() << "failed to get IWinInetHttpInfo from binding_"; | 675 NOTREACHED() << "failed to get IWinInetHttpInfo from binding_"; |
| 660 } | 676 } |
| 661 | 677 |
| 662 return http_status; | 678 return http_status; |
| 663 } | 679 } |
| 664 | 680 |
| 681 std::string UrlmonUrlRequest::GetHttpHeaders() const { |
| 682 if (binding_ == NULL) { |
| 683 DLOG(WARNING) << "GetHttpResponseStatus - no binding_"; |
| 684 return std::string(); |
| 685 } |
| 686 |
| 687 ScopedComPtr<IWinInetHttpInfo> info; |
| 688 if (FAILED(info.QueryFrom(binding_))) { |
| 689 DLOG(WARNING) << "Failed to QI for IWinInetHttpInfo"; |
| 690 return std::string(); |
| 691 } |
| 692 |
| 693 scoped_ptr<char> buffer; |
| 694 DWORD size = 0; |
| 695 DWORD flags = 0; |
| 696 DWORD reserved = 0; |
| 697 HRESULT hr = info->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, NULL, &size, |
| 698 &flags, &reserved); |
| 699 if (!size) { |
| 700 DLOG(WARNING) << "Failed to query HTTP headers size. Error 0x%x" << hr; |
| 701 return std::string(); |
| 702 } |
| 703 |
| 704 buffer.reset(new char[size]); |
| 705 memset(buffer.get(), 0, size); |
| 706 |
| 707 hr = info->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, buffer.get(), |
| 708 &size, &flags, &reserved); |
| 709 if (FAILED(hr)) { |
| 710 DLOG(WARNING) << "Failed to query HTTP headers. Error 0x%x" << hr; |
| 711 return std::string(); |
| 712 } |
| 713 |
| 714 return buffer.get(); |
| 715 } |
| 716 |
| 665 // | 717 // |
| 666 // UrlmonUrlRequest::Cache implementation. | 718 // UrlmonUrlRequest::Cache implementation. |
| 667 // | 719 // |
| 668 | 720 |
| 669 size_t UrlmonUrlRequest::Cache::Size() { | 721 size_t UrlmonUrlRequest::Cache::Size() { |
| 670 size_t size = 0; | 722 size_t size = 0; |
| 671 if (stream_) { | 723 if (stream_) { |
| 672 STATSTG cache_stat = {0}; | 724 STATSTG cache_stat = {0}; |
| 673 stream_->Stat(&cache_stat, STATFLAG_NONAME); | 725 stream_->Stat(&cache_stat, STATFLAG_NONAME); |
| 674 | 726 |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 825 ret = net::ERR_ACCESS_DENIED; | 877 ret = net::ERR_ACCESS_DENIED; |
| 826 break; | 878 break; |
| 827 | 879 |
| 828 default: | 880 default: |
| 829 DLOG(WARNING) | 881 DLOG(WARNING) |
| 830 << StringPrintf("TODO: translate HRESULT 0x%08X to net::Error", hr); | 882 << StringPrintf("TODO: translate HRESULT 0x%08X to net::Error", hr); |
| 831 break; | 883 break; |
| 832 } | 884 } |
| 833 return ret; | 885 return ret; |
| 834 } | 886 } |
| OLD | NEW |