// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_FRAME_URLMON_URL_REQUEST_PRIVATE_H_
#define CHROME_FRAME_URLMON_URL_REQUEST_PRIVATE_H_
#include <atlbase.h>
#include <atlcom.h>
#include <string>
#include "base/gtest_prod_util.h"
#include "base/threading/platform_thread.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request_status.h"
class UrlmonUrlRequest
: public CComObjectRootEx<CComMultiThreadModel>,
public PluginUrlRequest,
public IServiceProviderImpl<UrlmonUrlRequest>,
public IBindStatusCallback,
public IHttpNegotiate,
public IAuthenticate,
public IHttpSecurity {
public:
virtual bool Start();
virtual void Stop();
virtual bool Read(int bytes_to_read);
// Special function needed by ActiveDocument::Load()
HRESULT InitPending(const GURL& url, IMoniker* moniker, IBindCtx* bind_ctx,
bool enable_frame_busting, bool privileged_mode,
HWND notification_window, IStream* cache);
// Used from "DownloadRequestInHost".
// Callback will be invoked either right away (if operation is finished) or
// from inside ::OnStopBinding() when it is safe to reuse the bind_context.
typedef base::Callback<void(IMoniker*, IBindCtx*, IStream*, const char*)>
TerminateBindCallback;
void TerminateBind(const TerminateBindCallback& callback);
// Parent Window for UrlMon error dialogs
void set_parent_window(HWND parent_window) {
parent_window_ = parent_window;
}
// This function passes information on whether ChromeFrame is running in
// privileged mode.
void set_privileged_mode(bool privileged_mode) {
privileged_mode_ = privileged_mode;
}
// Returns a string in the form " id: %i Obj: %X URL: %s" which is useful
// to identify request objects in the log.
std::string me() const;
protected:
UrlmonUrlRequest();
~UrlmonUrlRequest();
BEGIN_COM_MAP(UrlmonUrlRequest)
COM_INTERFACE_ENTRY(IHttpNegotiate)
COM_INTERFACE_ENTRY(IServiceProvider)
COM_INTERFACE_ENTRY(IBindStatusCallback)
COM_INTERFACE_ENTRY(IWindowForBindingUI)
COM_INTERFACE_ENTRY(IAuthenticate)
COM_INTERFACE_ENTRY(IHttpSecurity)
END_COM_MAP()
BEGIN_SERVICE_MAP(UrlmonUrlRequest)
SERVICE_ENTRY(IID_IHttpNegotiate);
END_SERVICE_MAP()
// IBindStatusCallback implementation
STDMETHOD(OnStartBinding)(DWORD reserved, IBinding* binding);
STDMETHOD(GetPriority)(LONG* priority);
STDMETHOD(OnLowResource)(DWORD reserved);
STDMETHOD(OnProgress)(ULONG progress, ULONG max_progress,
ULONG status_code, LPCWSTR status_text);
STDMETHOD(OnStopBinding)(HRESULT result, LPCWSTR error);
STDMETHOD(GetBindInfo)(DWORD* bind_flags, BINDINFO* bind_info);
STDMETHOD(OnDataAvailable)(DWORD flags, DWORD size, FORMATETC* formatetc,
STGMEDIUM* storage);
STDMETHOD(OnObjectAvailable)(REFIID iid, IUnknown* object);
// IHttpNegotiate implementation
STDMETHOD(BeginningTransaction)(const wchar_t* url,
const wchar_t* current_headers, DWORD reserved,
wchar_t** additional_headers);
STDMETHOD(OnResponse)(DWORD dwResponseCode, const wchar_t* response_headers,
const wchar_t* request_headers, wchar_t** additional_headers);
// IWindowForBindingUI implementation. This interface is used typically to
// query the window handle which URLMON uses as the parent of error dialogs.
STDMETHOD(GetWindow)(REFGUID guid_reason, HWND* parent_window);
// IAuthenticate implementation. Used to return the parent window for the
// dialog displayed by IE for authenticating with a proxy.
STDMETHOD(Authenticate)(HWND* parent_window, LPWSTR* user_name,
LPWSTR* password);
// IHttpSecurity implementation.
STDMETHOD(OnSecurityProblem)(DWORD problem);
void set_pending(bool pending) {
pending_ = pending;
}
bool pending() const {
return pending_;
}
bool terminate_requested() const {
return !terminate_bind_callback_.is_null();
}
std::string response_headers() {
return response_headers_;
}
protected:
void ReleaseBindings();
HRESULT StartAsyncDownload();
void NotifyDelegateAndDie();
void TerminateTransaction();
static net::Error HresultToNetError(HRESULT hr);
private:
size_t SendDataToDelegate(size_t bytes);
// This class simplifies tracking the progress of operation. We have 3 main
// states: DONE, WORKING and ABORTING.
// When in [DONE] or [ABORTING] state, there is additional information
// about the result of operation.
// Start(), SetRedirected(), Cancel() and Done() methods trigger the state
// change. See comments bellow.
class Status {
public:
enum State {DONE, ABORTING, WORKING};
struct Redirection {
Redirection() : http_code(0) { }
int http_code;
std::string utf8_url;
};
Status() : state_(Status::DONE) {
}
State get_state() const {
return state_;
}
// Switch from [DONE] to [WORKING].
void Start() {
DCHECK_EQ(state_, DONE);
state_ = WORKING;
}
// Save redirection information and switch to [ABORTING] state.
// Assumes binding_->Abort() will be called!
void SetRedirected(int http_code, const std::string& utf8_url) {
DCHECK_EQ(state_, WORKING);
DCHECK_EQ(result_.status(), net::URLRequestStatus::SUCCESS);
redirect_.utf8_url = utf8_url;
// At times we receive invalid redirect codes like 0, 200, etc. We
// default to 302 in this case.
redirect_.http_code = http_code;
if (!net::HttpResponseHeaders::IsRedirectResponseCode(http_code))
redirect_.http_code = 302;
state_ = ABORTING;
}
// Set the result as net::URLRequestStatus::CANCELED.
// Switch to [ABORTING] state (if not already in that state).
void Cancel() {
if (state_ == DONE)
return;
if (state_ == WORKING) {
state_ = ABORTING;
} else {
// state_ == ABORTING
redirect_.http_code = 0;
redirect_.utf8_url.clear();
}
set_result(net::URLRequestStatus::CANCELED, 0);
}
void Done() {
state_ = DONE;
}
bool was_redirected() const {
return redirect_.http_code != 0;
}
const Redirection& get_redirection() const {
return redirect_;
}
const net::URLRequestStatus& get_result() const {
return result_;
}
void set_result(net::URLRequestStatus::Status status, int error) {
result_.set_status(status);
result_.set_error(error);
}
void set_result(HRESULT hr) {
result_.set_status(FAILED(hr)? net::URLRequestStatus::FAILED:
net::URLRequestStatus::SUCCESS);
result_.set_error(HresultToNetError(hr));
}
private:
Redirection redirect_;
State state_;
net::URLRequestStatus result_;
};
Status status_;
base::win::ScopedComPtr<IBinding> binding_;
base::win::ScopedComPtr<IMoniker> moniker_;
base::win::ScopedComPtr<IBindCtx> bind_context_;
base::win::ScopedComPtr<IStream> cache_;
base::win::ScopedComPtr<IStream> pending_data_;
size_t pending_read_size_;
base::PlatformThreadId thread_;
HWND parent_window_;
bool headers_received_;
int calling_delegate_; // re-entrancy protection.
// Set to true if the ChromeFrame instance is running in privileged mode.
bool privileged_mode_;
bool pending_;
TerminateBindCallback terminate_bind_callback_;
std::string response_headers_;
// Defaults to true and indicates whether we want to keep the original
// transaction alive when we receive the last data notification from
// urlmon.
bool is_expecting_download_;
// Set to true if the Urlmon transaction object needs to be cleaned up
// when this object is destroyed. Happens if we return
// INET_E_TERMINATE_BIND from OnDataAvailable in the last data notification.
bool cleanup_transaction_;
// Copy of the request headers.
std::string request_headers_;
DISALLOW_COPY_AND_ASSIGN(UrlmonUrlRequest);
};
#endif // CHROME_FRAME_URLMON_URL_REQUEST_PRIVATE_H_