| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Helper classes and functions used for the WebRequest API. |
| |
| #ifndef EXTENSIONS_BROWSER_API_WEB_REQUEST_WEB_REQUEST_API_HELPERS_H_ |
| #define EXTENSIONS_BROWSER_API_WEB_REQUEST_WEB_REQUEST_API_HELPERS_H_ |
| |
| #include <list> |
| #include <optional> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/memory/scoped_refptr.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "extensions/common/api/web_request.h" |
| #include "extensions/common/extension_id.h" |
| #include "net/base/auth.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/http/http_response_headers.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| class BrowserContext; |
| } |
| |
| namespace extensions { |
| class Extension; |
| struct WebRequestInfo; |
| |
| namespace declarative_net_request { |
| struct RequestAction; |
| } // namespace declarative_net_request |
| |
| } // namespace extensions |
| |
| namespace extension_web_request_api_helpers { |
| |
| using ResponseHeader = std::pair<std::string, std::string>; |
| using ResponseHeaders = std::vector<ResponseHeader>; |
| |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. |
| enum class RequestHeaderType { |
| kNone = 0, |
| kOther = 1, |
| kAccept = 2, |
| kAcceptCharset = 3, |
| kAcceptEncoding = 4, |
| kAcceptLanguage = 5, |
| kAccessControlRequestHeaders = 6, |
| kAccessControlRequestMethod = 7, |
| kAuthorization = 8, |
| kCacheControl = 9, |
| kConnection = 10, |
| kContentEncoding = 11, |
| kContentLanguage = 12, |
| kContentLength = 13, |
| kContentLocation = 14, |
| kContentType = 15, |
| kCookie = 16, |
| kDate = 17, |
| kDnt = 18, |
| kEarlyData = 19, |
| kExpect = 20, |
| kForwarded = 21, |
| kFrom = 22, |
| kHost = 23, |
| kIfMatch = 24, |
| kIfModifiedSince = 25, |
| kIfNoneMatch = 26, |
| kIfRange = 27, |
| kIfUnmodifiedSince = 28, |
| kKeepAlive = 29, |
| kOrigin = 30, |
| kPragma = 31, |
| kProxyAuthorization = 32, |
| kProxyConnection = 33, |
| kRange = 34, |
| kReferer = 35, |
| // kSecOriginPolicy = 36, // no longer shipping |
| kTe = 37, |
| kTransferEncoding = 38, |
| kUpgrade = 39, |
| kUpgradeInsecureRequests = 40, |
| kUserAgent = 41, |
| kVia = 42, |
| kWarning = 43, |
| kXForwardedFor = 44, |
| kXForwardedHost = 45, |
| kXForwardedProto = 46, |
| kMaxValue = kXForwardedProto, |
| }; |
| |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. |
| enum class ResponseHeaderType { |
| kNone = 0, |
| kOther = 1, |
| kAcceptPatch = 2, |
| kAcceptRanges = 3, |
| kAccessControlAllowCredentials = 4, |
| kAccessControlAllowHeaders = 5, |
| kAccessControlAllowMethods = 6, |
| kAccessControlAllowOrigin = 7, |
| kAccessControlExposeHeaders = 8, |
| kAccessControlMaxAge = 9, |
| kAge = 10, |
| kAllow = 11, |
| kAltSvc = 12, |
| kCacheControl = 13, |
| kClearSiteData = 14, |
| kConnection = 15, |
| kContentDisposition = 16, |
| kContentEncoding = 17, |
| kContentLanguage = 18, |
| kContentLength = 19, |
| kContentLocation = 20, |
| kContentRange = 21, |
| kContentSecurityPolicy = 22, |
| kContentSecurityPolicyReportOnly = 23, |
| kContentType = 24, |
| kDate = 25, |
| kETag = 26, |
| kExpectCT = 27, |
| kExpires = 28, |
| kFeaturePolicy = 29, |
| kKeepAlive = 30, |
| kLargeAllocation = 31, |
| kLastModified = 32, |
| kLocation = 33, |
| kPragma = 34, |
| kProxyAuthenticate = 35, |
| kProxyConnection = 36, |
| kPublicKeyPins = 37, |
| kPublicKeyPinsReportOnly = 38, |
| kReferrerPolicy = 39, |
| kRefresh = 40, |
| kRetryAfter = 41, |
| kSecWebSocketAccept = 42, |
| kServer = 43, |
| kServerTiming = 44, |
| kSetCookie = 45, |
| kSourceMap = 46, |
| kStrictTransportSecurity = 47, |
| kTimingAllowOrigin = 48, |
| kTk = 49, |
| kTrailer = 50, |
| kTransferEncoding = 51, |
| kUpgrade = 52, |
| kVary = 53, |
| kVia = 54, |
| kWarning = 55, |
| kWWWAuthenticate = 56, |
| kXContentTypeOptions = 57, |
| kXDNSPrefetchControl = 58, |
| kXFrameOptions = 59, |
| kXXSSProtection = 60, |
| kMaxValue = kXXSSProtection |
| }; |
| |
| struct IgnoredAction { |
| IgnoredAction(extensions::ExtensionId extension_id, |
| extensions::api::web_request::IgnoredActionType action_type); |
| IgnoredAction(const IgnoredAction&) = delete; |
| IgnoredAction(IgnoredAction&& rhs); |
| IgnoredAction& operator=(const IgnoredAction&) = delete; |
| |
| extensions::ExtensionId extension_id; |
| extensions::api::web_request::IgnoredActionType action_type; |
| }; |
| |
| using IgnoredActions = std::vector<IgnoredAction>; |
| |
| // Internal representation of the extraInfoSpec parameter on webRequest |
| // events, used to specify extra information to be included with network |
| // events. |
| struct ExtraInfoSpec { |
| enum Flags { |
| REQUEST_HEADERS = 1 << 0, |
| RESPONSE_HEADERS = 1 << 1, |
| BLOCKING = 1 << 2, |
| ASYNC_BLOCKING = 1 << 3, |
| REQUEST_BODY = 1 << 4, |
| EXTRA_HEADERS = 1 << 5, |
| }; |
| |
| static bool InitFromValue(content::BrowserContext* browser_context, |
| const base::Value& value, |
| int* extra_info_spec); |
| }; |
| |
| // Data container for RequestCookies as defined in the declarative WebRequest |
| // API definition. |
| struct RequestCookie { |
| RequestCookie(); |
| RequestCookie(const RequestCookie&) = delete; |
| RequestCookie(RequestCookie&& other); |
| RequestCookie& operator=(const RequestCookie&) = delete; |
| RequestCookie& operator=(RequestCookie&& other); |
| ~RequestCookie(); |
| |
| bool operator==(const RequestCookie& other) const; |
| |
| RequestCookie Clone() const; |
| |
| std::optional<std::string> name; |
| std::optional<std::string> value; |
| }; |
| |
| // Data container for ResponseCookies as defined in the declarative WebRequest |
| // API definition. |
| struct ResponseCookie { |
| ResponseCookie(); |
| ResponseCookie(const ResponseCookie&) = delete; |
| ResponseCookie(ResponseCookie&& other); |
| ResponseCookie& operator=(const ResponseCookie&) = delete; |
| ResponseCookie& operator=(ResponseCookie&& other); |
| ~ResponseCookie(); |
| |
| bool operator==(const ResponseCookie& other) const; |
| |
| ResponseCookie Clone() const; |
| |
| std::optional<std::string> name; |
| std::optional<std::string> value; |
| std::optional<std::string> expires; |
| std::optional<int> max_age; |
| std::optional<std::string> domain; |
| std::optional<std::string> path; |
| std::optional<bool> secure; |
| std::optional<bool> http_only; |
| }; |
| |
| // Data container for FilterResponseCookies as defined in the declarative |
| // WebRequest API definition. |
| struct FilterResponseCookie : ResponseCookie { |
| FilterResponseCookie(); |
| FilterResponseCookie(const FilterResponseCookie&) = delete; |
| FilterResponseCookie(FilterResponseCookie&& other); |
| FilterResponseCookie& operator=(const FilterResponseCookie&) = delete; |
| FilterResponseCookie& operator=(FilterResponseCookie&& other); |
| ~FilterResponseCookie(); |
| |
| FilterResponseCookie Clone() const; |
| |
| bool operator==(const FilterResponseCookie& other) const; |
| |
| std::optional<int> age_lower_bound; |
| std::optional<int> age_upper_bound; |
| std::optional<bool> session_cookie; |
| }; |
| |
| enum CookieModificationType { |
| ADD, |
| EDIT, |
| REMOVE, |
| }; |
| |
| struct RequestCookieModification { |
| RequestCookieModification(); |
| RequestCookieModification(const RequestCookieModification&) = delete; |
| RequestCookieModification(RequestCookieModification&& other); |
| RequestCookieModification& operator=(const RequestCookieModification&) = |
| delete; |
| RequestCookieModification& operator=(RequestCookieModification&& other); |
| ~RequestCookieModification(); |
| |
| bool operator==(const RequestCookieModification& other) const; |
| |
| RequestCookieModification Clone() const; |
| |
| CookieModificationType type; |
| // Used for EDIT and REMOVE, nullopt otherwise. |
| std::optional<RequestCookie> filter; |
| // Used for ADD and EDIT, nullopt otherwise. |
| std::optional<RequestCookie> modification; |
| }; |
| |
| struct ResponseCookieModification { |
| ResponseCookieModification(); |
| ResponseCookieModification(const ResponseCookieModification&) = delete; |
| ResponseCookieModification(ResponseCookieModification&& other); |
| ResponseCookieModification& operator=(const ResponseCookieModification&) = |
| delete; |
| ResponseCookieModification& operator=(ResponseCookieModification&& other); |
| ~ResponseCookieModification(); |
| |
| bool operator==(const ResponseCookieModification& other) const; |
| |
| ResponseCookieModification Clone() const; |
| |
| CookieModificationType type; |
| // Used for EDIT and REMOVE, nullopt otherwise. |
| std::optional<FilterResponseCookie> filter; |
| // Used for ADD and EDIT, nullopt otherwise. |
| std::optional<ResponseCookie> modification; |
| }; |
| |
| using RequestCookieModifications = std::vector<RequestCookieModification>; |
| using ResponseCookieModifications = std::vector<ResponseCookieModification>; |
| |
| // Contains the modification an extension wants to perform on an event. |
| struct EventResponseDelta { |
| EventResponseDelta(const extensions::ExtensionId& extension_id, |
| const base::Time& extension_install_time); |
| EventResponseDelta(const EventResponseDelta&) = delete; |
| EventResponseDelta(EventResponseDelta&& other); |
| EventResponseDelta& operator=(const EventResponseDelta&) = delete; |
| EventResponseDelta& operator=(EventResponseDelta&& other); |
| ~EventResponseDelta(); |
| |
| // ID of the extension that sent this response. |
| extensions::ExtensionId extension_id; |
| |
| // The time that the extension was installed. Used for deciding order of |
| // precedence in case multiple extensions respond with conflicting |
| // decisions. |
| base::Time extension_install_time; |
| |
| // Response values. These are mutually exclusive. |
| bool cancel; |
| GURL new_url; |
| |
| // Newly introduced or overridden request headers. |
| net::HttpRequestHeaders modified_request_headers; |
| |
| // Keys of request headers to be deleted. |
| std::vector<std::string> deleted_request_headers; |
| |
| // Headers that were added to the response. A modification of a header |
| // corresponds to a deletion and subsequent addition of the new header. |
| ResponseHeaders added_response_headers; |
| |
| // Headers that were deleted from the response. |
| ResponseHeaders deleted_response_headers; |
| |
| // Authentication Credentials to use. |
| std::optional<net::AuthCredentials> auth_credentials; |
| |
| // Modifications to cookies in request headers. |
| RequestCookieModifications request_cookie_modifications; |
| |
| // Modifications to cookies in response headers. |
| ResponseCookieModifications response_cookie_modifications; |
| |
| // Messages that shall be sent to the background/event/... pages of the |
| // extension. |
| std::set<std::string> messages_to_extension; |
| }; |
| |
| using EventResponseDeltas = std::list<EventResponseDelta>; |
| |
| // Comparison operator that returns true if the extension that caused |
| // |a| was installed after the extension that caused |b|. |
| bool InDecreasingExtensionInstallationTimeOrder(const EventResponseDelta& a, |
| const EventResponseDelta& b); |
| |
| // Converts a string to a list of integers, each in 0..255. |
| base::Value::List StringToCharList(const std::string& s); |
| |
| // Converts a list of integer values between 0 and 255 into a string |*out|. |
| // Returns true if the conversion was successful. |
| bool CharListToString(const base::Value::List& list, std::string* out); |
| |
| // The following functions calculate and return the modifications to requests |
| // commanded by extension handlers. All functions take the id of the extension |
| // that commanded a modification, the installation time of this extension (used |
| // for defining a precedence in conflicting modifications) and whether the |
| // extension requested to |cancel| the request. Other parameters depend on a |
| // the signal handler. |
| |
| EventResponseDelta CalculateOnBeforeRequestDelta( |
| const extensions::ExtensionId& extension_id, |
| const base::Time& extension_install_time, |
| bool cancel, |
| const GURL& new_url); |
| EventResponseDelta CalculateOnBeforeSendHeadersDelta( |
| content::BrowserContext* browser_context, |
| const extensions::ExtensionId& extension_id, |
| const base::Time& extension_install_time, |
| bool cancel, |
| net::HttpRequestHeaders* old_headers, |
| net::HttpRequestHeaders* new_headers, |
| int extra_info_spec); |
| EventResponseDelta CalculateOnHeadersReceivedDelta( |
| const extensions::ExtensionId& extension_id, |
| const base::Time& extension_install_time, |
| bool cancel, |
| const GURL& old_url, |
| const GURL& new_url, |
| const net::HttpResponseHeaders* old_response_headers, |
| ResponseHeaders* new_response_headers, |
| int extra_info_spec); |
| EventResponseDelta CalculateOnAuthRequiredDelta( |
| const extensions::ExtensionId& extension_id, |
| const base::Time& extension_install_time, |
| bool cancel, |
| std::optional<net::AuthCredentials> auth_credentials); |
| |
| // These functions merge the responses (the |deltas|) of request handlers. |
| // The |deltas| need to be sorted in decreasing order of precedence of |
| // extensions. In case extensions had |deltas| that could not be honored, their |
| // IDs are reported in |conflicting_extensions|. |
| |
| // Stores in |*canceled_by_extension| whether any extension wanted to cancel the |
| // request, std::nullopt if none did, the extension id otherwise. |
| void MergeCancelOfResponses( |
| const EventResponseDeltas& deltas, |
| std::optional<extensions::ExtensionId>* canceled_by_extension); |
| // Stores in |*new_url| the redirect request of the extension with highest |
| // precedence. Extensions that did not command to redirect the request are |
| // ignored in this logic. |
| void MergeRedirectUrlOfResponses(const GURL& url, |
| const EventResponseDeltas& deltas, |
| GURL* new_url, |
| IgnoredActions* ignored_actions); |
| // Stores in |*new_url| the redirect request of the extension with highest |
| // precedence. Extensions that did not command to redirect the request are |
| // ignored in this logic. |
| void MergeOnBeforeRequestResponses(const GURL& url, |
| const EventResponseDeltas& deltas, |
| GURL* new_url, |
| IgnoredActions* ignored_actions); |
| // Modifies the "Cookie" header in |request_headers| according to |
| // |deltas.request_cookie_modifications|. Conflicts are currently ignored |
| // silently. |
| void MergeCookiesInOnBeforeSendHeadersResponses( |
| const GURL& gurl, |
| const EventResponseDeltas& deltas, |
| net::HttpRequestHeaders* request_headers); |
| // Modifies the headers in |request_headers| according to |deltas|. Conflicts |
| // are tried to be resolved. |
| // Stores in |request_headers_modified| whether the request headers were |
| // modified. |
| // Any actions within |request.dnr_actions| which result in headers being |
| // modified are added to |matched_dnr_actions|. |
| void MergeOnBeforeSendHeadersResponses( |
| const extensions::WebRequestInfo& request, |
| const EventResponseDeltas& deltas, |
| net::HttpRequestHeaders* request_headers, |
| IgnoredActions* ignored_actions, |
| std::set<std::string>* removed_headers, |
| std::set<std::string>* set_headers, |
| bool* request_headers_modified, |
| std::vector<const extensions::declarative_net_request::RequestAction*>* |
| matched_dnr_actions); |
| // Modifies the "Set-Cookie" headers in |override_response_headers| according to |
| // |deltas.response_cookie_modifications|. If |override_response_headers| is |
| // NULL, a copy of |original_response_headers| is created. Conflicts are |
| // currently ignored silently. |
| void MergeCookiesInOnHeadersReceivedResponses( |
| const GURL& url, |
| const EventResponseDeltas& deltas, |
| const net::HttpResponseHeaders* original_response_headers, |
| scoped_refptr<net::HttpResponseHeaders>* override_response_headers); |
| // Stores a copy of |original_response_header| into |override_response_headers| |
| // that is modified according to |deltas|. If |deltas| does not instruct to |
| // modify the response headers, |override_response_headers| remains empty. |
| // Extension-initiated redirects are written to |override_response_headers| |
| // (to request redirection) and |*preserve_fragment_on_redirect_url| (to make |
| // sure that the URL provided by the extension isn't modified by having its |
| // fragment overwritten by that of the original URL). Stores in |
| // |response_headers_modified| whether the response headers were modified. |
| // Any actions within |request.dnr_actions| which result in headers being |
| // modified are added to |matched_dnr_actions|. |
| void MergeOnHeadersReceivedResponses( |
| const extensions::WebRequestInfo& request, |
| const EventResponseDeltas& deltas, |
| const net::HttpResponseHeaders* original_response_headers, |
| scoped_refptr<net::HttpResponseHeaders>* override_response_headers, |
| GURL* preserve_fragment_on_redirect_url, |
| IgnoredActions* ignored_actions, |
| bool* response_headers_modified, |
| std::vector<const extensions::declarative_net_request::RequestAction*>* |
| matched_dnr_actions); |
| // Merge the responses of blocked onAuthRequired handlers. The first |
| // registered listener that supplies authentication credentials in a response, |
| // if any, will have its authentication credentials used. |request| must be |
| // non-NULL, and contain |deltas| that are sorted in decreasing order of |
| // precedence. |
| // Returns whether authentication credentials are set. |
| bool MergeOnAuthRequiredResponses(const EventResponseDeltas& deltas, |
| net::AuthCredentials* auth_credentials, |
| IgnoredActions* ignored_actions); |
| |
| // Triggers clearing any back-forward caches and each renderer's in-memory cache |
| // the next time it navigates. |
| void ClearCacheOnNavigation(); |
| |
| // Converts the |name|, |value| pair of a http header to a HttpHeaders |
| // dictionary. |
| base::Value::Dict CreateHeaderDictionary(const std::string& name, |
| const std::string& value); |
| |
| // Returns whether a request header should be hidden from listeners. |
| bool ShouldHideRequestHeader(content::BrowserContext* browser_context, |
| int extra_info_spec, |
| const std::string& name); |
| |
| // Returns whether a response header should be hidden from listeners. |
| bool ShouldHideResponseHeader(int extra_info_spec, const std::string& name); |
| |
| // "Redirects" a request to `new_url` after the response has started by setting |
| // the appropriate headers in `override_response_headers`. |
| void RedirectRequestAfterHeadersReceived( |
| const GURL& new_url, |
| net::HttpResponseHeaders& override_response_headers, |
| GURL* preserve_fragment_on_redirect_url); |
| |
| } // namespace extension_web_request_api_helpers |
| |
| #endif // EXTENSIONS_BROWSER_API_WEB_REQUEST_WEB_REQUEST_API_HELPERS_H_ |