Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(183)

Side by Side Diff: chrome_frame/urlmon_url_request.cc

Issue 218019: Initial import of the Chrome Frame codebase. Integration in chrome.gyp coming... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome_frame/urlmon_url_request.h ('k') | chrome_frame/utils.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome_frame/urlmon_url_request.h"
6
7 #include <wininet.h>
8
9 #include "base/scoped_ptr.h"
10 #include "base/string_util.h"
11 #include "base/logging.h"
12 #include "chrome_frame/urlmon_upload_data_stream.h"
13 #include "net/http/http_util.h"
14 #include "net/http/http_response_headers.h"
15
16 static const LARGE_INTEGER kZero = {0};
17 static const ULARGE_INTEGER kUnsignedZero = {0};
18 int UrlmonUrlRequest::instance_count_ = 0;
19 const char kXFrameOptionsHeader[] = "X-Frame-Options";
20
21 UrlmonUrlRequest::UrlmonUrlRequest()
22 : pending_read_size_(0),
23 status_(URLRequestStatus::FAILED, net::ERR_FAILED),
24 thread_(PlatformThread::CurrentId()),
25 is_request_started_(false),
26 post_data_len_(0),
27 redirect_status_(0),
28 parent_window_(NULL) {
29 DLOG(INFO) << StringPrintf("Created request. Obj: %X", this)
30 << " Count: " << ++instance_count_;
31 }
32
33 UrlmonUrlRequest::~UrlmonUrlRequest() {
34 DLOG(INFO) << StringPrintf("Deleted request. Obj: %X", this)
35 << " Count: " << --instance_count_;
36 }
37
38 bool UrlmonUrlRequest::Start() {
39 DCHECK_EQ(PlatformThread::CurrentId(), thread_);
40
41 status_.set_status(URLRequestStatus::IO_PENDING);
42 HRESULT hr = StartAsyncDownload();
43 if (FAILED(hr)) {
44 // Do not call EndRequest() here since it will attempt to free references
45 // that have not been established.
46 status_.set_os_error(HresultToNetError(hr));
47 status_.set_status(URLRequestStatus::FAILED);
48 DLOG(ERROR) << "StartAsyncDownload failed";
49 OnResponseEnd(status_);
50 return false;
51 }
52
53 // Take a self reference to maintain COM lifetime. This will be released
54 // in EndRequest. Set a flag indicating that we have an additional
55 // reference here
56 is_request_started_ = true;
57 AddRef();
58 request_handler()->AddRequest(this);
59 return true;
60 }
61
62 void UrlmonUrlRequest::Stop() {
63 DCHECK_EQ(PlatformThread::CurrentId(), thread_);
64
65 if (binding_) {
66 binding_->Abort();
67 } else {
68 status_.set_status(URLRequestStatus::CANCELED);
69 status_.set_os_error(net::ERR_FAILED);
70 EndRequest();
71 }
72 }
73
74 bool UrlmonUrlRequest::Read(int bytes_to_read) {
75 DCHECK_EQ(PlatformThread::CurrentId(), thread_);
76
77 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this);
78
79 // Send cached data if available.
80 CComObjectStackEx<SendStream> send_stream;
81 send_stream.Initialize(this);
82 size_t bytes_copied = 0;
83 if (cached_data_.is_valid() && cached_data_.Read(&send_stream, bytes_to_read,
84 &bytes_copied)) {
85 DLOG(INFO) << StringPrintf("URL: %s Obj: %X - bytes read from cache: %d",
86 url().c_str(), this, bytes_copied);
87 return true;
88 }
89
90 // if the request is finished or there's nothing more to read
91 // then end the request
92 if (!status_.is_io_pending() || !binding_) {
93 DLOG(INFO) << StringPrintf("URL: %s Obj: %X. Response finished. Status: %d",
94 url().c_str(), this, status_.status());
95 EndRequest();
96 return true;
97 }
98
99 pending_read_size_ = bytes_to_read;
100 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) <<
101 "- Read pending for: " << bytes_to_read;
102 return true;
103 }
104
105 STDMETHODIMP UrlmonUrlRequest::OnStartBinding(
106 DWORD reserved, IBinding *binding) {
107 binding_ = binding;
108 return S_OK;
109 }
110
111 STDMETHODIMP UrlmonUrlRequest::GetPriority(LONG *priority) {
112 if (!priority)
113 return E_POINTER;
114 *priority = THREAD_PRIORITY_NORMAL;
115 return S_OK;
116 }
117
118 STDMETHODIMP UrlmonUrlRequest::OnLowResource(DWORD reserved) {
119 return S_OK;
120 }
121
122 STDMETHODIMP UrlmonUrlRequest::OnProgress(ULONG progress, ULONG max_progress,
123 ULONG status_code, LPCWSTR status_text) {
124 switch (status_code) {
125 case BINDSTATUS_REDIRECTING:
126 DCHECK(status_text != NULL);
127 DLOG(INFO) << "URL: " << url() << " redirected to "
128 << status_text;
129 redirect_url_ = status_text;
130 // Fetch the redirect status as they aren't all equal (307 in particular
131 // retains the HTTP request verb).
132 redirect_status_ = GetHttpResponseStatus();
133 break;
134
135 default:
136 DLOG(INFO) << " Obj: " << std::hex << this << " OnProgress(" << url()
137 << StringPrintf(L") code: %i status: %ls", status_code, status_text);
138 break;
139 }
140
141 return S_OK;
142 }
143
144 STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) {
145 DCHECK_EQ(PlatformThread::CurrentId(), thread_);
146
147 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) <<
148 " - Request stopped, Result: " << std::hex << result <<
149 " Status: " << status_.status();
150 if (FAILED(result)) {
151 status_.set_status(URLRequestStatus::FAILED);
152 status_.set_os_error(HresultToNetError(result));
153 EndRequest();
154 } else {
155 status_.set_status(URLRequestStatus::SUCCESS);
156 status_.set_os_error(0);
157 }
158
159 // Release these variables after reporting EndRequest since we might need to
160 // access their state.
161 binding_ = NULL;
162 bind_context_ = NULL;
163
164 return S_OK;
165 }
166
167 STDMETHODIMP UrlmonUrlRequest::GetBindInfo(DWORD* bind_flags,
168 BINDINFO *bind_info) {
169 DCHECK_EQ(PlatformThread::CurrentId(), thread_);
170
171 if ((bind_info == NULL) || (bind_info->cbSize == 0) || (bind_flags == NULL))
172 return E_INVALIDARG;
173
174 *bind_flags = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
175 if (LowerCaseEqualsASCII(method(), "get")) {
176 bind_info->dwBindVerb = BINDVERB_GET;
177 } else if (LowerCaseEqualsASCII(method(), "post")) {
178 bind_info->dwBindVerb = BINDVERB_POST;
179
180 // Bypass caching proxies on POSTs and avoid writing responses to POST
181 // requests to the browser's cache.
182 *bind_flags |= BINDF_GETNEWESTVERSION | BINDF_NOWRITECACHE |
183 BINDF_PRAGMA_NO_CACHE;
184
185 // Initialize the STGMEDIUM.
186 memset(&bind_info->stgmedData, 0, sizeof(STGMEDIUM));
187 bind_info->grfBindInfoF = 0;
188 bind_info->szCustomVerb = NULL;
189
190 scoped_refptr<net::UploadData> upload_data(upload_data());
191 post_data_len_ = upload_data.get() ? upload_data->GetContentLength() : 0;
192 if (post_data_len_) {
193 DLOG(INFO) << " Obj: " << std::hex << this << " POST request with "
194 << Int64ToString(post_data_len_) << " bytes";
195 CComObject<UrlmonUploadDataStream>* upload_stream = NULL;
196 HRESULT hr =
197 CComObject<UrlmonUploadDataStream>::CreateInstance(&upload_stream);
198 if (FAILED(hr)) {
199 NOTREACHED();
200 return hr;
201 }
202 upload_stream->Initialize(upload_data.get());
203
204 // Fill the STGMEDIUM with the data to post
205 bind_info->stgmedData.tymed = TYMED_ISTREAM;
206 bind_info->stgmedData.pstm = static_cast<IStream*>(upload_stream);
207 bind_info->stgmedData.pstm->AddRef();
208 } else {
209 DLOG(INFO) << " Obj: " << std::hex << this
210 << "POST request with no data!";
211 }
212 } else {
213 NOTREACHED() << "Unknown HTTP method.";
214 status_.set_status(URLRequestStatus::FAILED);
215 status_.set_os_error(net::ERR_INVALID_URL);
216 EndRequest();
217 return E_FAIL;
218 }
219
220 return S_OK;
221 }
222
223 STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size,
224 FORMATETC* formatetc,
225 STGMEDIUM* storage) {
226 DCHECK_EQ(PlatformThread::CurrentId(), thread_);
227 DLOG(INFO) << StringPrintf("URL: %s Obj: %X - Bytes available: %d",
228 url().c_str(), this, size);
229
230 if (!storage || (storage->tymed != TYMED_ISTREAM)) {
231 NOTREACHED();
232 return E_INVALIDARG;
233 }
234
235 IStream* read_stream = storage->pstm;
236 if (!read_stream) {
237 NOTREACHED();
238 return E_UNEXPECTED;
239 }
240
241 HRESULT hr = S_OK;
242 if (BSCF_FIRSTDATANOTIFICATION & flags) {
243 DCHECK(!cached_data_.is_valid());
244 cached_data_.Create();
245 }
246
247 // Always read data into cache. We have to read all the data here at this
248 // time or it won't be available later. Since the size of the data could
249 // be more than pending read size, it's not straightforward (or might even
250 // be impossible) to implement a true data pull model.
251 size_t bytes_available = 0;
252 cached_data_.Append(read_stream, &bytes_available);
253 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) <<
254 " - Bytes read into cache: " << bytes_available;
255
256 if (pending_read_size_) {
257 CComObjectStackEx<SendStream> send_stream;
258 send_stream.Initialize(this);
259 cached_data_.Read(&send_stream, pending_read_size_, &pending_read_size_);
260 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) <<
261 " - size read: " << pending_read_size_;
262 pending_read_size_ = 0;
263 } else {
264 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) <<
265 " - waiting for remote read";
266 }
267
268 if (BSCF_LASTDATANOTIFICATION & flags) {
269 status_.set_status(URLRequestStatus::SUCCESS);
270 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) <<
271 " - end of data.";
272 }
273
274 return S_OK;
275 }
276
277 STDMETHODIMP UrlmonUrlRequest::OnObjectAvailable(REFIID iid, IUnknown *object) {
278 // We are calling BindToStorage on the moniker we should always get called
279 // back on OnDataAvailable and should never get OnObjectAvailable
280 NOTREACHED();
281 return E_NOTIMPL;
282 }
283
284 STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url,
285 const wchar_t* current_headers, DWORD reserved,
286 wchar_t** additional_headers) {
287 DCHECK_EQ(PlatformThread::CurrentId(), thread_);
288 if (!additional_headers) {
289 NOTREACHED();
290 return E_POINTER;
291 }
292
293 DLOG(INFO) << "URL: " << url << " Obj: " << std::hex << this <<
294 " - Request headers: \n" << current_headers;
295
296 HRESULT hr = S_OK;
297
298 std::string new_headers;
299 if (post_data_len_ > 0) {
300 // Tack on the Content-Length header since when using an IStream type
301 // STGMEDIUM, it looks like it doesn't get set for us :(
302 new_headers = StringPrintf("Content-Length: %s\r\n",
303 Int64ToString(post_data_len_).c_str());
304 }
305
306 if (!extra_headers().empty()) {
307 // TODO(robertshield): We may need to sanitize headers on POST here.
308 new_headers += extra_headers();
309 }
310
311 if (!referrer().empty()) {
312 // Referrer is famously misspelled in HTTP:
313 new_headers += StringPrintf("Referer: %s\r\n", referrer().c_str());
314 }
315
316 if (!new_headers.empty()) {
317 *additional_headers = reinterpret_cast<wchar_t*>(
318 CoTaskMemAlloc((new_headers.size() + 1) * sizeof(wchar_t)));
319
320 if (*additional_headers == NULL) {
321 NOTREACHED();
322 hr = E_OUTOFMEMORY;
323 } else {
324 lstrcpynW(*additional_headers, ASCIIToWide(new_headers).c_str(),
325 new_headers.size());
326 }
327 }
328
329 return hr;
330 }
331
332 STDMETHODIMP UrlmonUrlRequest::OnResponse(DWORD dwResponseCode,
333 const wchar_t* response_headers, const wchar_t* request_headers,
334 wchar_t** additional_headers) {
335 DCHECK_EQ(PlatformThread::CurrentId(), thread_);
336
337 std::string raw_headers = WideToUTF8(response_headers);
338
339 // Security check for frame busting headers. We don't honor the headers
340 // as-such, but instead simply kill requests which we've been asked to
341 // look for. This puts the onus on the user of the UrlRequest to specify
342 // whether or not requests should be inspected. For ActiveDocuments, the
343 // answer is "no", since WebKit's detection/handling is sufficient and since
344 // ActiveDocuments cannot be hosted as iframes. For NPAPI and ActiveX
345 // documents, the Initialize() function of the PluginUrlRequest object
346 // allows them to specify how they'd like requests handled. Both should
347 // set enable_frame_busting_ to true to avoid CSRF attacks.
348 // Should WebKit's handling of this ever change, we will need to re-visit
349 // how and when frames are killed to better mirror a policy which may
350 // do something other than kill the sub-document outright.
351
352 // NOTE(slightlyoff): We don't use net::HttpResponseHeaders here because
353 // of lingering ICU/base_noicu issues.
354 if (frame_busting_enabled_ &&
355 net::HttpUtil::HasHeader(raw_headers, kXFrameOptionsHeader)) {
356 DLOG(ERROR) << "X-Frame-Options header detected, navigation canceled";
357 return E_FAIL;
358 }
359
360 std::wstring url_for_persistent_cookies =
361 redirect_url_.empty() ? UTF8ToWide(url()) : redirect_url_;
362
363 std::string persistent_cookies;
364
365 DWORD cookie_size = 0; // NOLINT
366 InternetGetCookie(url_for_persistent_cookies.c_str(), NULL, NULL,
367 &cookie_size);
368 if (cookie_size) {
369 scoped_ptr<wchar_t> cookies(new wchar_t[cookie_size + 1]);
370 if (!InternetGetCookie(url_for_persistent_cookies.c_str(), NULL,
371 cookies.get(), &cookie_size)) {
372 NOTREACHED() << "InternetGetCookie failed. Error: " << GetLastError();
373 } else {
374 persistent_cookies = WideToUTF8(cookies.get());
375 }
376 }
377
378 OnResponseStarted("",
379 raw_headers.c_str(),
380 0,
381 base::Time(),
382 persistent_cookies,
383 redirect_url_.empty() ? std::string() :
384 WideToUTF8(redirect_url_),
385 redirect_status_);
386
387 return S_OK;
388 }
389
390 STDMETHODIMP UrlmonUrlRequest::GetWindow(const GUID& guid_reason,
391 HWND* parent_window) {
392 if (!parent_window) {
393 return E_INVALIDARG;
394 }
395
396 #ifndef NDEBUG
397 wchar_t guid[40] = {0};
398 ::StringFromGUID2(guid_reason, guid, arraysize(guid));
399
400 DLOG(INFO) << " Obj: " << std::hex << this << " GetWindow: " <<
401 (guid_reason == IID_IAuthenticate ? L" - IAuthenticate" :
402 (guid_reason == IID_IHttpSecurity ? L"IHttpSecurity" :
403 (guid_reason == IID_IWindowForBindingUI ? L"IWindowForBindingUI" :
404 guid)));
405 #endif
406
407 // TODO(iyengar): This hits when running the URL request tests.
408 DLOG_IF(ERROR, !::IsWindow(parent_window_))
409 << "UrlmonUrlRequest::GetWindow - no window!";
410 *parent_window = parent_window_;
411 return S_OK;
412 }
413
414 STDMETHODIMP UrlmonUrlRequest::Authenticate(HWND* parent_window,
415 LPWSTR* user_name,
416 LPWSTR* password) {
417 if (!parent_window) {
418 return E_INVALIDARG;
419 }
420
421 DCHECK(::IsWindow(parent_window_));
422 *parent_window = parent_window_;
423 return S_OK;
424 }
425
426 STDMETHODIMP UrlmonUrlRequest::OnSecurityProblem(DWORD problem) {
427 // Urlmon notifies the client of authentication problems, certificate
428 // errors, etc by querying the object implementing the IBindStatusCallback
429 // interface for the IHttpSecurity interface. If this interface is not
430 // implemented then Urlmon checks for the problem codes defined below
431 // and performs actions as defined below:-
432 // It invokes the ReportProgress method of the protocol sink with
433 // these problem codes and eventually invokes the ReportResult method
434 // on the protocol sink which ends up in a call to the OnStopBinding
435 // method of the IBindStatusCallBack interface.
436
437 // MSHTML's implementation of the IBindStatusCallback interface does not
438 // implement the IHttpSecurity interface. However it handles the
439 // OnStopBinding call with a HRESULT of 0x800c0019 and navigates to
440 // an interstitial page which presents the user with a choice of whether
441 // to abort the navigation.
442
443 // In our OnStopBinding implementation we stop the navigation and inform
444 // Chrome about the result. Ideally Chrome should behave in a manner similar
445 // to IE, i.e. display the SSL error interstitial page and if the user
446 // decides to proceed anyway we would turn off SSL warnings for that
447 // particular navigation and allow IE to download the content.
448 // We would need to return the certificate information to Chrome for display
449 // purposes. Currently we only return a dummy certificate to Chrome.
450 // At this point we decided that it is a lot of work at this point and
451 // decided to go with the easier option of implementing the IHttpSecurity
452 // interface and replicating the checks performed by Urlmon. This
453 // causes Urlmon to display a dialog box on the same lines as IE6.
454 DLOG(INFO) << __FUNCTION__ << " Security problem : " << problem;
455
456 HRESULT hr = E_ABORT;
457
458 switch (problem) {
459 case ERROR_INTERNET_SEC_CERT_REV_FAILED: {
460 hr = RPC_E_RETRY;
461 break;
462 }
463
464 case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
465 case ERROR_INTERNET_SEC_CERT_CN_INVALID:
466 case ERROR_INTERNET_INVALID_CA: {
467 hr = S_FALSE;
468 break;
469 }
470
471 default: {
472 NOTREACHED() << "Unhandled security problem : " << problem;
473 break;
474 }
475 }
476 return hr;
477 }
478
479 HRESULT UrlmonUrlRequest::ConnectToExistingMoniker(IMoniker* moniker,
480 IBindCtx* context,
481 const std::wstring& url) {
482 if (!moniker || url.empty()) {
483 NOTREACHED() << "Invalid arguments";
484 return E_INVALIDARG;
485 }
486
487 DCHECK(moniker_.get() == NULL);
488 DCHECK(bind_context_.get() == NULL);
489
490 moniker_ = moniker;
491 bind_context_ = context;
492 set_url(WideToUTF8(url));
493 return S_OK;
494 }
495
496 HRESULT UrlmonUrlRequest::StartAsyncDownload() {
497 HRESULT hr = E_FAIL;
498 if (moniker_.get() == NULL) {
499 std::wstring wide_url = UTF8ToWide(url());
500 hr = CreateURLMonikerEx(NULL, wide_url.c_str(), moniker_.Receive(),
501 URL_MK_UNIFORM);
502 if (FAILED(hr)) {
503 NOTREACHED() << "CreateURLMonikerEx failed. Error: " << hr;
504 } else {
505 hr = CreateAsyncBindCtx(0, this, NULL, bind_context_.Receive());
506 DCHECK(SUCCEEDED(hr)) << "CreateAsyncBindCtx failed. Error: " << hr;
507 }
508 } else {
509 DCHECK(bind_context_.get() != NULL);
510 hr = RegisterBindStatusCallback(bind_context_, this, NULL, 0);
511 }
512
513 if (SUCCEEDED(hr)) {
514 ScopedComPtr<IStream> stream;
515 hr = moniker_->BindToStorage(bind_context_, NULL, __uuidof(IStream),
516 reinterpret_cast<void**>(stream.Receive()));
517 if (FAILED(hr)) {
518 // TODO(joshia): Look into. This currently fails for:
519 // http://user2:secret@localhost:1337/auth-basic?set-cookie-if-challenged
520 // when running the UrlRequest unit tests.
521 DLOG(ERROR) <<
522 StringPrintf("IUrlMoniker::BindToStorage failed. Error: 0x%08X.", hr)
523 << std::endl << url();
524 DCHECK(hr == MK_E_SYNTAX);
525 }
526 }
527
528 DLOG_IF(ERROR, FAILED(hr))
529 << StringPrintf(L"StartAsyncDownload failed: 0x%08X", hr);
530
531 return hr;
532 }
533
534 void UrlmonUrlRequest::EndRequest() {
535 DLOG(INFO) << __FUNCTION__;
536 // Special case. If the last request was a redirect and the current OS
537 // error value is E_ACCESSDENIED, that means an unsafe redirect was attempted.
538 // In that case, correct the OS error value to be the more specific
539 // ERR_UNSAFE_REDIRECT error value.
540 if (!status_.is_success() && status_.os_error() == net::ERR_ACCESS_DENIED) {
541 int status = GetHttpResponseStatus();
542 if (status >= 300 && status < 400) {
543 redirect_status_ = status; // store the latest redirect status value.
544 status_.set_os_error(net::ERR_UNSAFE_REDIRECT);
545 }
546 }
547 request_handler()->RemoveRequest(this);
548 OnResponseEnd(status_);
549
550 // If the request was started then we must have an additional reference on the
551 // request.
552 if (is_request_started_) {
553 is_request_started_ = false;
554 Release();
555 }
556 }
557
558 int UrlmonUrlRequest::GetHttpResponseStatus() const {
559 if (binding_ == NULL) {
560 DLOG(WARNING) << "GetHttpResponseStatus - no binding_";
561 return 0;
562 }
563
564 int http_status = 0;
565
566 ScopedComPtr<IWinInetHttpInfo> info;
567 if (SUCCEEDED(info.QueryFrom(binding_))) {
568 char status[10] = {0};
569 DWORD buf_size = sizeof(status);
570 if (SUCCEEDED(info->QueryInfo(HTTP_QUERY_STATUS_CODE, status, &buf_size,
571 0, NULL))) {
572 http_status = StringToInt(status);
573 } else {
574 NOTREACHED() << "Failed to get HTTP status";
575 }
576 } else {
577 NOTREACHED() << "failed to get IWinInetHttpInfo from binding_";
578 }
579
580 return http_status;
581 }
582
583 //
584 // UrlmonUrlRequest::Cache implementation.
585 //
586
587 size_t UrlmonUrlRequest::Cache::Size() {
588 size_t size = 0;
589 if (stream_) {
590 STATSTG cache_stat = {0};
591 stream_->Stat(&cache_stat, STATFLAG_NONAME);
592
593 DCHECK_EQ(0, cache_stat.cbSize.HighPart);
594 size = cache_stat.cbSize.LowPart;
595 }
596
597 return size;
598 }
599
600 size_t UrlmonUrlRequest::Cache::CurrentPos() {
601 size_t pos = 0;
602 if (stream_) {
603 ULARGE_INTEGER current_index = {0};
604 stream_->Seek(kZero, STREAM_SEEK_CUR, &current_index);
605
606 DCHECK_EQ(0, current_index.HighPart);
607 pos = current_index.LowPart;
608 }
609
610 return pos;
611 }
612
613 size_t UrlmonUrlRequest::Cache::SizeRemaining() {
614 size_t size = Size();
615 size_t pos = CurrentPos();
616 size_t size_remaining = 0;
617
618 if (size) {
619 DCHECK(pos <= size);
620 size_remaining = size - pos;
621 }
622 return size_remaining;
623 }
624
625 void UrlmonUrlRequest::Cache::Clear() {
626 if (!stream_) {
627 NOTREACHED();
628 return;
629 }
630
631 HRESULT hr = stream_->SetSize(kUnsignedZero);
632 DCHECK(SUCCEEDED(hr));
633 }
634
635 bool UrlmonUrlRequest::Cache::Read(IStream* dest, size_t size,
636 size_t* bytes_copied) {
637 if (!dest || !size) {
638 NOTREACHED();
639 return false;
640 }
641
642 // Copy the data and clear cache if there is no more data to copy.
643 ULARGE_INTEGER size_to_copy = {size, 0};
644 ULARGE_INTEGER size_written = {0};
645 stream_->CopyTo(dest, size_to_copy, NULL, &size_written);
646
647 if (size_written.LowPart && bytes_copied)
648 *bytes_copied = size_written.LowPart;
649
650 if (!SizeRemaining()) {
651 Clear();
652 stream_->Seek(kZero, STREAM_SEEK_SET, NULL);
653 }
654
655 return (size_written.LowPart != 0);
656 }
657
658 bool UrlmonUrlRequest::Cache::Append(IStream* source,
659 size_t* bytes_copied) {
660 if (!source) {
661 NOTREACHED();
662 return false;
663 }
664
665 size_t current_pos = CurrentPos();
666 stream_->Seek(kZero, STREAM_SEEK_END, NULL);
667
668 HRESULT hr = S_OK;
669 while (SUCCEEDED(hr)) {
670 DWORD chunk_read = 0; // NOLINT
671 hr = source->Read(read_buffer_, sizeof(read_buffer_), &chunk_read);
672 if (!chunk_read)
673 break;
674
675 DWORD chunk_written = 0; // NOLINT
676 stream_->Write(read_buffer_, chunk_read, &chunk_written);
677 DCHECK_EQ(chunk_read, chunk_written);
678
679 if (bytes_copied)
680 *bytes_copied += chunk_written;
681 }
682
683 LARGE_INTEGER last_read_position = {current_pos, 0};
684 stream_->Seek(last_read_position, STREAM_SEEK_SET, NULL);
685 return SUCCEEDED(hr);
686 }
687
688 bool UrlmonUrlRequest::Cache::Create() {
689 DCHECK(stream_ == NULL);
690 bool ret = SUCCEEDED(CreateStreamOnHGlobal(NULL, TRUE, stream_.Receive()));
691 DCHECK(ret && stream_);
692 return ret;
693 }
694
695 net::Error UrlmonUrlRequest::HresultToNetError(HRESULT hr) {
696 // Useful reference:
697 // http://msdn.microsoft.com/en-us/library/ms775145(VS.85).aspx
698
699 net::Error ret = net::ERR_UNEXPECTED;
700
701 switch (hr) {
702 case S_OK:
703 ret = net::OK;
704 break;
705
706 case MK_E_SYNTAX:
707 ret = net::ERR_INVALID_URL;
708 break;
709
710 case INET_E_CANNOT_CONNECT:
711 ret = net::ERR_CONNECTION_FAILED;
712 break;
713
714 case INET_E_DOWNLOAD_FAILURE:
715 case INET_E_CONNECTION_TIMEOUT:
716 case E_ABORT:
717 ret = net::ERR_CONNECTION_ABORTED;
718 break;
719
720 case INET_E_DATA_NOT_AVAILABLE:
721 ret = net::ERR_EMPTY_RESPONSE;
722 break;
723
724 case INET_E_RESOURCE_NOT_FOUND:
725 // To behave more closely to the chrome network stack, we translate this
726 // error value as tunnel connection failed. This error value is tested
727 // in the ProxyTunnelRedirectTest and UnexpectedServerAuthTest tests.
728 ret = net::ERR_TUNNEL_CONNECTION_FAILED;
729 break;
730
731 case INET_E_INVALID_URL:
732 case INET_E_UNKNOWN_PROTOCOL:
733 case INET_E_REDIRECT_FAILED:
734 ret = net::ERR_INVALID_URL;
735 break;
736
737 case INET_E_INVALID_CERTIFICATE:
738 ret = net::ERR_CERT_INVALID;
739 break;
740
741 case E_ACCESSDENIED:
742 ret = net::ERR_ACCESS_DENIED;
743 break;
744
745 default:
746 DLOG(WARNING)
747 << StringPrintf("TODO: translate HRESULT 0x%08X to net::Error", hr);
748 break;
749 }
750 return ret;
751 }
OLDNEW
« no previous file with comments | « chrome_frame/urlmon_url_request.h ('k') | chrome_frame/utils.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698