blob: c0e7e931e2f11e6e9eb02d01ec729acfa1e88946 [file] [log] [blame]
// Copyright (c) 2006-2008 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.
// This is the browser side of the resource dispatcher, it receives requests
// from the RenderProcessHosts, and dispatches them to URLRequests. It then
// fowards the messages from the URLRequests back to the correct process for
// handling.
//
// See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading
#ifndef CHROME_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_
#define CHROME_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_
#include <map>
#include <string>
#include "base/logging.h"
#include "base/observer_list.h"
#include "base/ref_counted.h"
#include "base/timer.h"
#include "chrome/browser/renderer_host/resource_handler.h"
#include "chrome/common/filter_policy.h"
#include "chrome/common/ipc_message.h"
#include "net/url_request/url_request.h"
#include "webkit/glue/resource_type.h"
class CrossSiteResourceHandler;
class DownloadFileManager;
class DownloadRequestManager;
class LoginHandler;
class MessageLoop;
class PluginService;
class SafeBrowsingService;
class SaveFileManager;
class URLRequestContext;
struct ViewHostMsg_Resource_Request;
class ResourceDispatcherHost : public URLRequest::Delegate {
public:
// Implemented by the client of ResourceDispatcherHost to receive messages in
// response to a resource load. The messages are intended to be forwarded to
// the ResourceDispatcher in the renderer process via an IPC channel that the
// client manages.
//
// NOTE: This class unfortunately cannot be named 'Delegate' because that
// conflicts with the name of ResourceDispatcherHost's base class.
//
// If the receiver is unable to send a given message (i.e., if Send returns
// false), then the ResourceDispatcherHost assumes the receiver has failed,
// and the given request will be dropped. (This happens, for example, when a
// renderer crashes and the channel dies).
typedef IPC::Message::Sender Receiver;
// Holds the data we would like to associate with each request
class ExtraRequestInfo : public URLRequest::UserData {
friend class ResourceDispatcherHost;
public:
ExtraRequestInfo(ResourceHandler* handler,
int request_id,
int render_process_host_id,
int render_view_id,
bool mixed_content,
ResourceType::Type resource_type,
uint64 upload_size)
: resource_handler(handler),
cross_site_handler(NULL),
login_handler(NULL),
request_id(request_id),
render_process_host_id(render_process_host_id),
render_view_id(render_view_id),
pending_data_count(0),
is_download(false),
pause_count(0),
mixed_content(mixed_content),
resource_type(resource_type),
filter_policy(FilterPolicy::DONT_FILTER),
last_load_state(net::LOAD_STATE_IDLE),
upload_size(upload_size),
last_upload_position(0),
waiting_for_upload_progress_ack(false),
is_paused(false),
has_started_reading(false),
paused_read_bytes(0) {
}
// Top-level ResourceHandler servicing this request.
scoped_refptr<ResourceHandler> resource_handler;
// CrossSiteResourceHandler for this request, if it is a cross-site request.
// (NULL otherwise.) This handler is part of the chain of ResourceHandlers
// pointed to by resource_handler.
CrossSiteResourceHandler* cross_site_handler;
LoginHandler* login_handler;
int request_id;
int render_process_host_id;
int render_view_id;
int pending_data_count;
// Downloads allowed only as a top level request.
bool allow_download;
// Whether this is a download.
bool is_download;
// The number of clients that have called pause on this request.
int pause_count;
// Whether this request is served over HTTP and the main page was served
// over HTTPS.
bool mixed_content;
ResourceType::Type resource_type;
// Whether the content for this request should be filtered (on the renderer
// side) to make it more secure: images are stamped, frame content is
// replaced with an error message and all other resources are entirely
// filtered out.
FilterPolicy::Type filter_policy;
net::LoadState last_load_state;
uint64 upload_size;
uint64 last_upload_position;
base::TimeTicks last_upload_ticks;
bool waiting_for_upload_progress_ack;
private:
// Request is temporarily not handling network data. Should be used only
// by the ResourceDispatcherHost, not the event handlers.
bool is_paused;
// Whether this request has started reading any bytes from the response
// yet. Will be true after the first (unpaused) call to Read.
bool has_started_reading;
// How many bytes have been read while this request has been paused.
int paused_read_bytes;
};
class Observer {
public:
virtual void OnRequestStarted(ResourceDispatcherHost* resource_dispatcher,
URLRequest* request) = 0;
virtual void OnResponseCompleted(ResourceDispatcherHost* resource_dispatcher,
URLRequest* request) = 0;
virtual void OnReceivedRedirect(ResourceDispatcherHost* resource_dispatcher,
URLRequest* request,
const GURL& new_url) = 0;
};
// Uniquely identifies a URLRequest.
struct GlobalRequestID {
GlobalRequestID() : render_process_host_id(-1), request_id(-1) {
}
GlobalRequestID(int render_process_host_id, int request_id)
: render_process_host_id(render_process_host_id),
request_id(request_id) {
}
int render_process_host_id;
int request_id;
bool operator<(const GlobalRequestID& other) const {
if (render_process_host_id == other.render_process_host_id)
return request_id < other.request_id;
return render_process_host_id < other.render_process_host_id;
}
};
explicit ResourceDispatcherHost(MessageLoop* io_loop);
~ResourceDispatcherHost();
void Initialize();
// Puts the resource dispatcher host in an inactive state (unable to begin
// new requests). Cancels all pending requests.
void Shutdown();
// Begins a resource request with the given params on behalf of the specified
// render view. Responses will be dispatched through the given receiver. The
// RenderProcessHost ID is used to lookup TabContents from routing_id's.
// request_context is the cookie/cache context to be used for this request.
//
// If sync_result is non-null, then a SyncLoad reply will be generated, else
// a normal asynchronous set of response messages will be generated.
//
void BeginRequest(Receiver* receiver,
HANDLE render_process_handle,
int render_process_host_id,
int render_view_id,
int request_id,
const ViewHostMsg_Resource_Request& request,
URLRequestContext* request_context,
IPC::Message* sync_result);
// Initiate a download from the browser process (as opposed to a resource
// request from the renderer).
void BeginDownload(const GURL& url,
const GURL& referrer,
int render_process_host_id,
int render_view_id,
URLRequestContext* request_context);
// Initiate a save file from the browser process (as opposed to a resource
// request from the renderer).
void BeginSaveFile(const GURL& url,
const GURL& referrer,
int render_process_host_id,
int render_view_id,
URLRequestContext* request_context);
// Cancels the given request if it still exists. We ignore cancels from the
// renderer in the event of a download.
void CancelRequest(int render_process_host_id,
int request_id,
bool from_renderer);
// Decrements the pending_data_count for the request and resumes
// the request if it was paused due to too many pending data
// messages sent.
void OnDataReceivedACK(int render_process_host_id, int request_id);
// Resets the waiting_for_upload_progress_ack flag.
void OnUploadProgressACK(int render_process_host_id, int request_id);
// Returns true if it's ok to send the data. If there are already too many
// data messages pending, it pauses the request and returns false. In this
// case the caller should not send the data.
bool WillSendData(int render_process_host_id, int request_id);
// Pauses or resumes network activity for a particular request.
void PauseRequest(int render_process_host_id, int request_id, bool pause);
// Returns the number of pending requests. This is designed for the unittests
int pending_requests() const {
return static_cast<int>(pending_requests_.size());
}
DownloadFileManager* download_file_manager() const {
return download_file_manager_;
}
DownloadRequestManager* download_request_manager() const {
return download_request_manager_.get();
}
SaveFileManager* save_file_manager() const {
return save_file_manager_;
}
SafeBrowsingService* safe_browsing_service() const {
return safe_browsing_;
}
MessageLoop* ui_loop() const { return ui_loop_; }
// Called when the onunload handler for a cross-site request has finished.
void OnClosePageACK(int render_process_host_id, int request_id);
// Force cancels any pending requests for the given process.
void CancelRequestsForProcess(int render_process_host_id);
// Force cancels any pending requests for the given render view. This method
// acts like CancelRequestsForProcess when render_view_id is -1.
void CancelRequestsForRenderView(int render_process_host_id,
int render_view_id);
// URLRequest::Delegate
virtual void OnReceivedRedirect(URLRequest* request,
const GURL& new_url);
virtual void OnAuthRequired(URLRequest* request,
net::AuthChallengeInfo* auth_info);
virtual void OnSSLCertificateError(URLRequest* request,
int cert_error,
net::X509Certificate* cert);
virtual void OnResponseStarted(URLRequest* request);
virtual void OnReadCompleted(URLRequest* request, int bytes_read);
void OnResponseCompleted(URLRequest* request);
// Helper function to get our extra data out of a request. The given request
// must have been one we created so that it has the proper extra data pointer.
static ExtraRequestInfo* ExtraInfoForRequest(URLRequest* request) {
ExtraRequestInfo* r = static_cast<ExtraRequestInfo*>(request->user_data());
DLOG_IF(WARNING, !r) << "Request doesn't seem to have our data";
return r;
}
static const ExtraRequestInfo* ExtraInfoForRequest(const URLRequest* request) {
const ExtraRequestInfo* r =
static_cast<const ExtraRequestInfo*>(request->user_data());
DLOG_IF(WARNING, !r) << "Request doesn't seem to have our data";
return r;
}
// Add an observer. The observer will be called on the IO thread. To
// observe resource events on the UI thread, subscribe to the
// NOTIFY_RESOURCE_* notifications of the notification service.
void AddObserver(Observer* obs);
// Remove an observer.
void RemoveObserver(Observer* obs);
// Retrieves a URLRequest. Must be called from the IO thread.
URLRequest* GetURLRequest(GlobalRequestID request_id) const;
// A test to determining whether a given request should be forwarded to the
// download thread.
bool ShouldDownload(const std::string& mime_type,
const std::string& content_disposition);
// Notify our observers that a request has been cancelled.
void NotifyResponseCompleted(URLRequest* request, int render_process_host_id);
void RemovePendingRequest(int render_process_host_id, int request_id);
private:
class ShutdownTask;
friend class ShutdownTask;
// A shutdown helper that runs on the IO thread.
void OnShutdown();
// Returns true if the request is paused.
bool PauseRequestIfNeeded(ExtraRequestInfo* info);
// Resumes the given request by calling OnResponseStarted or OnReadCompleted.
void ResumeRequest(const GlobalRequestID& request_id);
// Reads data from the response using our internal buffer as async IO.
// Returns true if data is available immediately, false otherwise. If the
// return value is false, we will receive a OnReadComplete() callback later.
bool Read(URLRequest* request, int* bytes_read);
// Internal function to finish an async IO which has completed. Returns
// true if there is more data to read (e.g. we haven't read EOF yet and
// no errors have occurred).
bool CompleteRead(URLRequest *, int* bytes_read);
// Internal function to finish handling the ResponseStarted message. Returns
// true on success.
bool CompleteResponseStarted(URLRequest* request);
// Cancels the given request if it still exists. We ignore cancels from the
// renderer in the event of a download. If |allow_delete| is true and no IO
// is pending, the request is removed and deleted.
void CancelRequest(int render_process_host_id,
int request_id,
bool from_renderer,
bool allow_delete);
// Helper function for regular and download requests.
void BeginRequestInternal(URLRequest* request, bool mixed_content);
// The list of all requests that we have pending. This list is not really
// optimized, and assumes that we have relatively few requests pending at once
// since some operations require brute-force searching of the list.
//
// It may be enhanced in the future to provide some kind of prioritization
// mechanism. We should also consider a hashtable or binary tree if it turns
// out we have a lot of things here.
typedef std::map<GlobalRequestID,URLRequest*> PendingRequestList;
// Deletes the pending request identified by the iterator passed in.
// This function will invalidate the iterator passed in. Callers should
// not rely on this iterator being valid on return.
void RemovePendingRequest(const PendingRequestList::iterator& iter);
// Notify our observers that we started receiving a response for a request.
void NotifyResponseStarted(URLRequest* request, int render_process_host_id);
// Notify our observers that a request has been redirected.
void NofityReceivedRedirect(URLRequest* request,
int render_process_host_id,
const GURL& new_url);
// Tries to handle the url with an external protocol. If the request is
// handled, the function returns true. False otherwise.
bool HandleExternalProtocol(int request_id,
int render_process_host_id,
int tab_contents_id,
const GURL& url,
ResourceType::Type resource_type,
ResourceHandler* handler);
void UpdateLoadStates();
void MaybeUpdateUploadProgress(ExtraRequestInfo *info, URLRequest *request);
PendingRequestList pending_requests_;
// We cache the UI message loop so we can create new UI-related objects on it.
MessageLoop* ui_loop_;
// We cache the IO loop to ensure that GetURLRequest is only called from the
// IO thread.
MessageLoop* io_loop_;
// A timer that periodically calls UpdateLoadStates while pending_requests_
// is not empty.
base::RepeatingTimer<ResourceDispatcherHost> update_load_states_timer_;
// We own the download file writing thread and manager
scoped_refptr<DownloadFileManager> download_file_manager_;
// Determines whether a download is allowed.
scoped_refptr<DownloadRequestManager> download_request_manager_;
// We own the save file manager.
scoped_refptr<SaveFileManager> save_file_manager_;
scoped_refptr<SafeBrowsingService> safe_browsing_;
// Request ID for non-renderer initiated requests. request_ids generated by
// the renderer process are counted up from 0, while browser created requests
// start at -2 and go down from there. (We need to start at -2 because -1 is
// used as a special value all over the resource_dispatcher_host for
// uninitialized variables.) This way, we no longer have the unlikely (but
// observed in the real world!) event where we have two requests with the same
// request_id_.
int request_id_;
// List of objects observing resource dispatching.
ObserverList<Observer> observer_list_;
PluginService* plugin_service_;
// For running tasks.
ScopedRunnableMethodFactory<ResourceDispatcherHost> method_runner_;
// True if the resource dispatcher host has been shut down.
bool is_shutdown_;
DISALLOW_COPY_AND_ASSIGN(ResourceDispatcherHost);
};
#endif // CHROME_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_