blob: 6255f8fff66d9f44ce0dadd59776205d5ce4f691 [file] [log] [blame]
// Copyright 2017 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 CONTENT_COMMON_THROTTLING_URL_LOADER_H_
#define CONTENT_COMMON_THROTTLING_URL_LOADER_H_
#include <memory>
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_piece.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/common/content_export.h"
#include "content/common/possibly_associated_interface_ptr.h"
#include "content/public/common/url_loader_throttle.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
namespace base {
class SingleThreadTaskRunner;
}
namespace content {
// ThrottlingURLLoader is a wrapper around the
// network::mojom::URLLoader[Factory] interfaces. It applies a list of
// URLLoaderThrottle instances which could defer, resume restart or cancel the
// URL loading. If the Mojo connection fails during the request it is canceled
// with net::ERR_ABORTED.
class CONTENT_EXPORT ThrottlingURLLoader
: public network::mojom::URLLoaderClient {
public:
// |client| must stay alive during the lifetime of the returned object. Please
// note that the request may not start immediately since it could be deferred
// by throttles.
static std::unique_ptr<ThrottlingURLLoader> CreateLoaderAndStart(
scoped_refptr<network::SharedURLLoaderFactory> factory,
std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
int32_t routing_id,
int32_t request_id,
uint32_t options,
network::ResourceRequest* url_request,
network::mojom::URLLoaderClient* client,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
~ThrottlingURLLoader() override;
void FollowRedirect(const std::vector<std::string>& removed_headers,
const net::HttpRequestHeaders& modified_headers);
// Follows a redirect, calling CreateLoaderAndStart() on the factory. This
// is useful if the factory uses different loaders for different URLs.
void FollowRedirectForcingRestart();
void SetPriority(net::RequestPriority priority, int32_t intra_priority_value);
// Restarts the load immediately with |factory| and |url_loader_options|.
// It must only be called when the following conditions are met:
// 1. The request already started and the original factory decided to not
// handle the request. This condition is required because throttles are not
// consulted prior to restarting.
// 2. The original factory did not call URLLoaderClient callbacks (e.g.,
// OnReceiveResponse).
// This function is useful in the case of service worker network fallback.
void RestartWithFactory(
scoped_refptr<network::SharedURLLoaderFactory> factory,
uint32_t url_loader_options);
// Disconnect the forwarding URLLoaderClient and the URLLoader. Returns the
// datapipe endpoints.
network::mojom::URLLoaderClientEndpointsPtr Unbind();
// Sets the forwarding client to receive all subsequent notifications.
void set_forwarding_client(network::mojom::URLLoaderClient* client) {
forwarding_client_ = client;
}
bool response_intercepted() const { return response_intercepted_; }
private:
class ForwardingThrottleDelegate;
ThrottlingURLLoader(
std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
network::mojom::URLLoaderClient* client,
const net::NetworkTrafficAnnotationTag& traffic_annotation);
void Start(scoped_refptr<network::SharedURLLoaderFactory> factory,
int32_t routing_id,
int32_t request_id,
uint32_t options,
network::ResourceRequest* url_request,
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
void StartNow();
void RestartWithFlagsNow();
// Processes the result of a URLLoaderThrottle call, adding the throttle to
// the blocking set if it deferred and updating |*should_defer| accordingly.
// Returns |true| if the request should continue to be processed (regardless
// of whether it's been deferred) or |false| if it's been cancelled.
bool HandleThrottleResult(URLLoaderThrottle* throttle,
bool throttle_deferred,
bool* should_defer);
// Stops a given throttle from deferring the request. If this was not the last
// deferring throttle, the request remains deferred. Otherwise it resumes
// progress.
void StopDeferringForThrottle(URLLoaderThrottle* throttle);
void RestartWithFlags(int additional_load_flags);
// network::mojom::URLLoaderClient implementation:
void OnReceiveResponse(
const network::ResourceResponseHead& response_head) override;
void OnReceiveRedirect(
const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& response_head) override;
void OnUploadProgress(int64_t current_position,
int64_t total_size,
OnUploadProgressCallback ack_callback) override;
void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override;
void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
void OnStartLoadingResponseBody(
mojo::ScopedDataPipeConsumerHandle body) override;
void OnComplete(const network::URLLoaderCompletionStatus& status) override;
void OnClientConnectionError();
void CancelWithError(int error_code, base::StringPiece custom_reason);
void Resume();
void SetPriority(net::RequestPriority priority);
void UpdateDeferredResponseHead(
const network::ResourceResponseHead& new_response_head);
void PauseReadingBodyFromNet(URLLoaderThrottle* throttle);
void ResumeReadingBodyFromNet(URLLoaderThrottle* throttle);
void InterceptResponse(
network::mojom::URLLoaderPtr new_loader,
network::mojom::URLLoaderClientRequest new_client_request,
network::mojom::URLLoaderPtr* original_loader,
network::mojom::URLLoaderClientRequest* original_client_request);
// Disconnects the client connection and releases the URLLoader.
void DisconnectClient(base::StringPiece custom_description);
enum DeferredStage {
DEFERRED_NONE,
DEFERRED_START,
DEFERRED_REDIRECT,
DEFERRED_BEFORE_RESPONSE,
DEFERRED_RESPONSE
};
DeferredStage deferred_stage_ = DEFERRED_NONE;
bool loader_completed_ = false;
struct ThrottleEntry {
ThrottleEntry(ThrottlingURLLoader* loader,
std::unique_ptr<URLLoaderThrottle> the_throttle);
ThrottleEntry(ThrottleEntry&& other);
~ThrottleEntry();
ThrottleEntry& operator=(ThrottleEntry&& other);
std::unique_ptr<ForwardingThrottleDelegate> delegate;
std::unique_ptr<URLLoaderThrottle> throttle;
private:
DISALLOW_COPY_AND_ASSIGN(ThrottleEntry);
};
std::vector<ThrottleEntry> throttles_;
std::set<URLLoaderThrottle*> deferring_throttles_;
std::set<URLLoaderThrottle*> pausing_reading_body_from_net_throttles_;
// NOTE: This may point to a native implementation (instead of a Mojo proxy
// object). And it is possible that the implementation of |forwarding_client_|
// destroys this object synchronously when this object is calling into it.
network::mojom::URLLoaderClient* forwarding_client_;
mojo::Binding<network::mojom::URLLoaderClient> client_binding_;
network::mojom::URLLoaderPtr url_loader_;
struct StartInfo {
StartInfo(
scoped_refptr<network::SharedURLLoaderFactory> in_url_loader_factory,
int32_t in_routing_id,
int32_t in_request_id,
uint32_t in_options,
network::ResourceRequest* in_url_request,
scoped_refptr<base::SingleThreadTaskRunner> in_task_runner);
~StartInfo();
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
int32_t routing_id;
int32_t request_id;
uint32_t options;
network::ResourceRequest url_request;
// |task_runner_| is used to set up |client_binding_|.
scoped_refptr<base::SingleThreadTaskRunner> task_runner;
};
// Holds any info needed to start or restart the request. Used when start is
// deferred or when FollowRedirectForcingRestart() is called.
std::unique_ptr<StartInfo> start_info_;
struct ResponseInfo {
explicit ResponseInfo(
const network::ResourceResponseHead& in_response_head);
~ResponseInfo();
network::ResourceResponseHead response_head;
};
// Set if response is deferred.
std::unique_ptr<ResponseInfo> response_info_;
struct RedirectInfo {
RedirectInfo(const net::RedirectInfo& in_redirect_info,
const network::ResourceResponseHead& in_response_head);
~RedirectInfo();
net::RedirectInfo redirect_info;
network::ResourceResponseHead response_head;
};
// Set if redirect is deferred.
std::unique_ptr<RedirectInfo> redirect_info_;
struct PriorityInfo {
PriorityInfo(net::RequestPriority in_priority,
int32_t in_intra_priority_value);
~PriorityInfo();
net::RequestPriority priority;
int32_t intra_priority_value;
};
// Set if request is deferred and SetPriority() is called.
std::unique_ptr<PriorityInfo> priority_info_;
// Set if a throttle changed the URL in WillStartRequest.
GURL throttle_will_start_redirect_url_;
// Set if a throttle changed the URL in WillRedirectRequest.
// Only supported with the network service.
GURL throttle_will_redirect_redirect_url_;
const net::NetworkTrafficAnnotationTag traffic_annotation_;
uint32_t inside_delegate_calls_ = 0;
// The latest request URL from where we expect a response
GURL response_url_;
bool response_intercepted_ = false;
std::vector<std::string> removed_headers_;
net::HttpRequestHeaders modified_headers_;
int pending_restart_flags_ = 0;
bool has_pending_restart_ = false;
base::WeakPtrFactory<ThrottlingURLLoader> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ThrottlingURLLoader);
};
} // namespace content
#endif // CONTENT_COMMON_THROTTLING_URL_LOADER_H_