From 1d515adc22edda4cf829409e91b65050136f3316 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Mon, 14 Oct 2019 13:32:38 +0200 Subject: [PATCH] Modify CefResponse header methods to match CefRequest API (fixes issue #2770) --- .gitignore | 1 + cef_paths2.gypi | 2 + include/capi/cef_response_capi.h | 17 ++++-- include/cef_api_hash.h | 10 ++-- include/cef_response.h | 12 ++++- libcef/common/net/http_header_utils.cc | 17 ++++++ libcef/common/net/http_header_utils.h | 12 ++++- libcef/common/request_impl.cc | 24 ++------- libcef/common/response_impl.cc | 35 +++++++++++-- libcef/common/response_impl.h | 5 +- libcef_dll/cpptoc/response_cpptoc.cc | 31 +++++++++-- libcef_dll/ctocpp/response_ctocpp.cc | 29 +++++++++-- libcef_dll/ctocpp/response_ctocpp.h | 7 ++- tests/ceftests/cookie_unittest.cc | 2 +- tests/ceftests/navigation_unittest.cc | 6 ++- tests/ceftests/response_unittest.cc | 71 ++++++++++++++++++++++++++ 16 files changed, 231 insertions(+), 50 deletions(-) create mode 100644 tests/ceftests/response_unittest.cc diff --git a/.gitignore b/.gitignore index ad9452e5d..c2f0cec2d 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ .metadata .project .pydevproject +.vscode # Settings directory for eclipse /.settings .checkstyle diff --git a/cef_paths2.gypi b/cef_paths2.gypi index 28d26ddb8..e78648dfa 100644 --- a/cef_paths2.gypi +++ b/cef_paths2.gypi @@ -490,6 +490,7 @@ 'tests/ceftests/request_context_unittest.cc', 'tests/ceftests/request_handler_unittest.cc', 'tests/ceftests/request_unittest.cc', + 'tests/ceftests/response_unittest.cc', 'tests/ceftests/resource.h', 'tests/ceftests/resource_manager_unittest.cc', 'tests/ceftests/resource_request_handler_unittest.cc', @@ -564,6 +565,7 @@ 'tests/ceftests/process_message_unittest.cc', 'tests/ceftests/request_handler_unittest.cc', 'tests/ceftests/request_unittest.cc', + 'tests/ceftests/response_unittest.cc', 'tests/ceftests/resource_request_handler_unittest.cc', 'tests/ceftests/routing_test_handler.cc', 'tests/ceftests/routing_test_handler.h', diff --git a/include/capi/cef_response_capi.h b/include/capi/cef_response_capi.h index 1b07738ea..b5109426b 100644 --- a/include/capi/cef_response_capi.h +++ b/include/capi/cef_response_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=cc5ec5ca76adb568adb08c3b58fb3289a94b2ecd$ +// $hash=67776173c2e8c45b7261af692af96084cdd618ec$ // #ifndef CEF_INCLUDE_CAPI_CEF_RESPONSE_CAPI_H_ @@ -126,8 +126,19 @@ typedef struct _cef_response_t { // Get the value for the specified response header field. /// // The resulting string must be freed by calling cef_string_userfree_free(). - cef_string_userfree_t(CEF_CALLBACK* get_header)(struct _cef_response_t* self, - const cef_string_t* name); + cef_string_userfree_t(CEF_CALLBACK* get_header_by_name)( + struct _cef_response_t* self, + const cef_string_t* name); + + /// + // Set the header |name| to |value|. If |overwrite| is true (1) any existing + // values will be replaced with the new value. If |overwrite| is false (0) any + // existing values will not be overwritten. + /// + void(CEF_CALLBACK* set_header_by_name)(struct _cef_response_t* self, + const cef_string_t* name, + const cef_string_t* value, + int overwrite); /// // Get all response header fields. diff --git a/include/cef_api_hash.h b/include/cef_api_hash.h index f030f44b7..f9f892a71 100644 --- a/include/cef_api_hash.h +++ b/include/cef_api_hash.h @@ -34,7 +34,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=e1836e5dade0a6f19f058dbfd55df881f454d1e3$ +// $hash=42f531d8211496faa76ffa838a2cf0b9cfa45c56$ // #ifndef CEF_INCLUDE_API_HASH_H_ @@ -47,13 +47,13 @@ // way that may cause binary incompatibility with other builds. The universal // hash value will change if any platform is affected whereas the platform hash // values will change only if that particular platform is affected. -#define CEF_API_HASH_UNIVERSAL "14afb7265af06a50b900ddd775adc235dc34ec77" +#define CEF_API_HASH_UNIVERSAL "842f5e9a25404c0d6d6408d361c760f1f107b129" #if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "228f8ff0c24b688aa36db6f0bf88b0c301738c1a" +#define CEF_API_HASH_PLATFORM "f691a75408d8ea7742a14db07fc1df73555320a3" #elif defined(OS_MACOSX) -#define CEF_API_HASH_PLATFORM "dd1c23009c1356fcb066f195af3da9fcf3175799" +#define CEF_API_HASH_PLATFORM "1c2b7de4554429cfb71302d465cb55e6da45788d" #elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "ebc0f044eab1238ff313ad4496dd2ff22bd261ec" +#define CEF_API_HASH_PLATFORM "698c65121d173f8a72997e91783166a930c2efca" #endif #ifdef __cplusplus diff --git a/include/cef_response.h b/include/cef_response.h index 70dee994a..f871c117b 100644 --- a/include/cef_response.h +++ b/include/cef_response.h @@ -127,7 +127,17 @@ class CefResponse : public virtual CefBaseRefCounted { // Get the value for the specified response header field. /// /*--cef()--*/ - virtual CefString GetHeader(const CefString& name) = 0; + virtual CefString GetHeaderByName(const CefString& name) = 0; + + /// + // Set the header |name| to |value|. If |overwrite| is true any existing + // values will be replaced with the new value. If |overwrite| is false any + // existing values will not be overwritten. + /// + /*--cef(optional_param=value)--*/ + virtual void SetHeaderByName(const CefString& name, + const CefString& value, + bool overwrite) = 0; /// // Get all response header fields. diff --git a/libcef/common/net/http_header_utils.cc b/libcef/common/net/http_header_utils.cc index 25db19304..6199d2f94 100644 --- a/libcef/common/net/http_header_utils.cc +++ b/libcef/common/net/http_header_utils.cc @@ -4,6 +4,9 @@ #include "libcef/common/net/http_header_utils.h" +#include + +#include "base/strings/string_util.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" @@ -40,4 +43,18 @@ void ParseHeaders(const std::string& header_str, HeaderMap& map) { } } +void MakeASCIILower(std::string* str) { + std::transform(str->begin(), str->end(), str->begin(), ::tolower); +} + +HeaderMap::iterator FindHeaderInMap(const std::string& nameLower, + HeaderMap& map) { + for (auto it = map.begin(); it != map.end(); ++it) { + if (base::EqualsCaseInsensitiveASCII(it->first.ToString(), nameLower)) + return it; + } + + return map.end(); +} + } // namespace HttpHeaderUtils diff --git a/libcef/common/net/http_header_utils.h b/libcef/common/net/http_header_utils.h index bbd981ecb..50a12370d 100644 --- a/libcef/common/net/http_header_utils.h +++ b/libcef/common/net/http_header_utils.h @@ -6,17 +6,25 @@ #define CEF_LIBCEF_COMMON_NET_HTTP_HEADER_UTILS_H_ #pragma once +#include #include -#include "include/cef_request.h" +#include "include/cef_base.h" namespace HttpHeaderUtils { -typedef CefRequest::HeaderMap HeaderMap; +typedef std::multimap HeaderMap; std::string GenerateHeaders(const HeaderMap& map); void ParseHeaders(const std::string& header_str, HeaderMap& map); +// Convert |str| to lower-case. +void MakeASCIILower(std::string* str); + +// Finds the first instance of |name| (already lower-case) in |map| with +// case-insensitive comparison. +HeaderMap::iterator FindHeaderInMap(const std::string& name, HeaderMap& map); + } // namespace HttpHeaderUtils #endif // CEF_LIBCEF_COMMON_NET_HTTP_HEADER_UTILS_H_ diff --git a/libcef/common/request_impl.cc b/libcef/common/request_impl.cc index fbba0ade2..2575f7ece 100644 --- a/libcef/common/request_impl.cc +++ b/libcef/common/request_impl.cc @@ -100,10 +100,6 @@ class FileElementReader : public net::UploadFileElementReader { DISALLOW_COPY_AND_ASSIGN(FileElementReader); }; -void MakeASCIILower(std::string* str) { - std::transform(str->begin(), str->end(), str->begin(), ::tolower); -} - // Returns the cef_urlrequest_flags_t policy specified by the Cache-Control // request header directives, if any. The directives are case-insensitive and // some have an optional argument. Multiple directives are comma-separated. @@ -127,7 +123,7 @@ int GetCacheControlHeaderPolicy(CefRequest::HeaderMap headerMap) { int flags = 0; if (!line.empty()) { - MakeASCIILower(&line); + HttpHeaderUtils::MakeASCIILower(&line); std::vector pieces = base::SplitStringPiece( line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); @@ -221,18 +217,6 @@ void SetHeaderMap(const CefRequest::HeaderMap& map, } } -// Finds the first instance of |nameLower| (already lower-case) in |map| with -// case-insensitive comparison. -CefRequest::HeaderMap::iterator FindHeaderInMap(const std::string& nameLower, - CefRequest::HeaderMap& map) { - for (auto it = map.begin(); it != map.end(); ++it) { - if (base::EqualsCaseInsensitiveASCII(it->first.ToString(), nameLower)) - return it; - } - - return map.end(); -} - // Type used in UploadDataStream. typedef std::vector> UploadElementReaders; @@ -362,9 +346,9 @@ CefString CefRequestImpl::GetHeaderByName(const CefString& name) { base::AutoLock lock_scope(lock_); std::string nameLower = name; - MakeASCIILower(&nameLower); + HttpHeaderUtils::MakeASCIILower(&nameLower); - auto it = FindHeaderInMap(nameLower, headermap_); + auto it = HttpHeaderUtils::FindHeaderInMap(nameLower, headermap_); if (it != headermap_.end()) return it->second; @@ -378,7 +362,7 @@ void CefRequestImpl::SetHeaderByName(const CefString& name, CHECK_READONLY_RETURN_VOID(); std::string nameLower = name; - MakeASCIILower(&nameLower); + HttpHeaderUtils::MakeASCIILower(&nameLower); // Do not include Referer in the header map. if (nameLower == kReferrerLowerCase) diff --git a/libcef/common/response_impl.cc b/libcef/common/response_impl.cc index 4a0ccfb33..007255942 100644 --- a/libcef/common/response_impl.cc +++ b/libcef/common/response_impl.cc @@ -6,6 +6,7 @@ #include +#include "libcef/common/net/http_header_utils.h" #include "libcef/common/net_service/net_service_util.h" #include "base/logging.h" @@ -96,16 +97,40 @@ void CefResponseImpl::SetCharset(const CefString& charset) { charset_ = charset; } -CefString CefResponseImpl::GetHeader(const CefString& name) { +CefString CefResponseImpl::GetHeaderByName(const CefString& name) { base::AutoLock lock_scope(lock_); - CefString value; + std::string nameLower = name; + HttpHeaderUtils::MakeASCIILower(&nameLower); - HeaderMap::const_iterator it = header_map_.find(name); + auto it = HttpHeaderUtils::FindHeaderInMap(nameLower, header_map_); if (it != header_map_.end()) - value = it->second; + return it->second; - return value; + return CefString(); +} + +void CefResponseImpl::SetHeaderByName(const CefString& name, + const CefString& value, + bool overwrite) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + + std::string nameLower = name; + HttpHeaderUtils::MakeASCIILower(&nameLower); + + // There may be multiple values, so remove any first. + for (auto it = header_map_.begin(); it != header_map_.end();) { + if (base::EqualsCaseInsensitiveASCII(it->first.ToString(), nameLower)) { + if (!overwrite) + return; + it = header_map_.erase(it); + } else { + ++it; + } + } + + header_map_.insert(std::make_pair(name, value)); } CefString CefResponseImpl::GetURL() { diff --git a/libcef/common/response_impl.h b/libcef/common/response_impl.h index 2f090cee8..da39db8f2 100644 --- a/libcef/common/response_impl.h +++ b/libcef/common/response_impl.h @@ -35,7 +35,10 @@ class CefResponseImpl : public CefResponse { void SetMimeType(const CefString& mimeType) override; CefString GetCharset() override; void SetCharset(const CefString& charset) override; - CefString GetHeader(const CefString& name) override; + CefString GetHeaderByName(const CefString& name) override; + void SetHeaderByName(const CefString& name, + const CefString& value, + bool overwrite) override; void GetHeaderMap(HeaderMap& headerMap) override; void SetHeaderMap(const HeaderMap& headerMap) override; CefString GetURL() override; diff --git a/libcef_dll/cpptoc/response_cpptoc.cc b/libcef_dll/cpptoc/response_cpptoc.cc index 25dcff4b9..f2f19d4e9 100644 --- a/libcef_dll/cpptoc/response_cpptoc.cc +++ b/libcef_dll/cpptoc/response_cpptoc.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=5c6b14610ef7bcadf5e4936a262c660896a32526$ +// $hash=f4a89951c1d04bd2c48dceed80e10b1e281a1d24$ // #include "libcef_dll/cpptoc/response_cpptoc.h" @@ -182,7 +182,8 @@ void CEF_CALLBACK response_set_charset(struct _cef_response_t* self, } cef_string_userfree_t CEF_CALLBACK -response_get_header(struct _cef_response_t* self, const cef_string_t* name) { +response_get_header_by_name(struct _cef_response_t* self, + const cef_string_t* name) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING DCHECK(self); @@ -194,12 +195,33 @@ response_get_header(struct _cef_response_t* self, const cef_string_t* name) { return NULL; // Execute - CefString _retval = CefResponseCppToC::Get(self)->GetHeader(CefString(name)); + CefString _retval = + CefResponseCppToC::Get(self)->GetHeaderByName(CefString(name)); // Return type: string return _retval.DetachToUserFree(); } +void CEF_CALLBACK response_set_header_by_name(struct _cef_response_t* self, + const cef_string_t* name, + const cef_string_t* value, + int overwrite) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: name; type: string_byref_const + DCHECK(name); + if (!name) + return; + // Unverified params: value + + // Execute + CefResponseCppToC::Get(self)->SetHeaderByName( + CefString(name), CefString(value), overwrite ? true : false); +} + void CEF_CALLBACK response_get_header_map(struct _cef_response_t* self, cef_string_multimap_t headerMap) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -288,7 +310,8 @@ CefResponseCppToC::CefResponseCppToC() { GetStruct()->set_mime_type = response_set_mime_type; GetStruct()->get_charset = response_get_charset; GetStruct()->set_charset = response_set_charset; - GetStruct()->get_header = response_get_header; + GetStruct()->get_header_by_name = response_get_header_by_name; + GetStruct()->set_header_by_name = response_set_header_by_name; GetStruct()->get_header_map = response_get_header_map; GetStruct()->set_header_map = response_set_header_map; GetStruct()->get_url = response_get_url; diff --git a/libcef_dll/ctocpp/response_ctocpp.cc b/libcef_dll/ctocpp/response_ctocpp.cc index 7015ec910..6f95fffcb 100644 --- a/libcef_dll/ctocpp/response_ctocpp.cc +++ b/libcef_dll/ctocpp/response_ctocpp.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=0c197661e7e3e905b90bec127ff7b1ff23240fa2$ +// $hash=40590f49f3e6fb36f98cf2e88558def58d41b58e$ // #include "libcef_dll/ctocpp/response_ctocpp.h" @@ -185,9 +185,9 @@ void CefResponseCToCpp::SetCharset(const CefString& charset) { } NO_SANITIZE("cfi-icall") -CefString CefResponseCToCpp::GetHeader(const CefString& name) { +CefString CefResponseCToCpp::GetHeaderByName(const CefString& name) { cef_response_t* _struct = GetStruct(); - if (CEF_MEMBER_MISSING(_struct, get_header)) + if (CEF_MEMBER_MISSING(_struct, get_header_by_name)) return CefString(); // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -199,7 +199,7 @@ CefString CefResponseCToCpp::GetHeader(const CefString& name) { // Execute cef_string_userfree_t _retval = - _struct->get_header(_struct, name.GetStruct()); + _struct->get_header_by_name(_struct, name.GetStruct()); // Return type: string CefString _retvalStr; @@ -207,6 +207,27 @@ CefString CefResponseCToCpp::GetHeader(const CefString& name) { return _retvalStr; } +NO_SANITIZE("cfi-icall") +void CefResponseCToCpp::SetHeaderByName(const CefString& name, + const CefString& value, + bool overwrite) { + cef_response_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, set_header_by_name)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: name; type: string_byref_const + DCHECK(!name.empty()); + if (name.empty()) + return; + // Unverified params: value + + // Execute + _struct->set_header_by_name(_struct, name.GetStruct(), value.GetStruct(), + overwrite); +} + NO_SANITIZE("cfi-icall") void CefResponseCToCpp::GetHeaderMap(HeaderMap& headerMap) { cef_response_t* _struct = GetStruct(); diff --git a/libcef_dll/ctocpp/response_ctocpp.h b/libcef_dll/ctocpp/response_ctocpp.h index 766dd16ad..5c6632d9a 100644 --- a/libcef_dll/ctocpp/response_ctocpp.h +++ b/libcef_dll/ctocpp/response_ctocpp.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=529b0d3676e398d5047eb714a8210fefcc937e76$ +// $hash=8ebf9fb3b9657402c801523ad86ca9773511a86b$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_RESPONSE_CTOCPP_H_ @@ -45,7 +45,10 @@ class CefResponseCToCpp : public CefCToCppRefCountedGetURL(); - const std::string& set_cookie_str = response->GetHeader("Set-Cookie"); + const std::string& set_cookie_str = response->GetHeaderByName("Set-Cookie"); // Expect the network cookie with URL1 requests only. if (resource_response_ct_ <= 2) { diff --git a/tests/ceftests/navigation_unittest.cc b/tests/ceftests/navigation_unittest.cc index 67d553866..36f9b371f 100644 --- a/tests/ceftests/navigation_unittest.cc +++ b/tests/ceftests/navigation_unittest.cc @@ -848,7 +848,8 @@ class RedirectTestHandler : public TestHandler { EXPECT_EQ(302, response->GetStatus()); EXPECT_STREQ("Found", response->GetStatusText().ToString().c_str()); EXPECT_STREQ("", response->GetMimeType().ToString().c_str()); - EXPECT_STREQ(kRNav2, response->GetHeader("Location").ToString().c_str()); + EXPECT_STREQ(kRNav2, + response->GetHeaderByName("Location").ToString().c_str()); // Change the redirect to the 3rd URL. new_url = kRNav3; @@ -860,7 +861,8 @@ class RedirectTestHandler : public TestHandler { EXPECT_STREQ("Internal Redirect", response->GetStatusText().ToString().c_str()); EXPECT_TRUE(response->GetMimeType().empty()); - EXPECT_STREQ(kRNav3, response->GetHeader("Location").ToString().c_str()); + EXPECT_STREQ(kRNav3, + response->GetHeaderByName("Location").ToString().c_str()); } else if (old_url == kRNav3 && new_url == kRNav4) { // Called due to the nav3 redirect response. got_nav3_redirect_.yes(); diff --git a/tests/ceftests/response_unittest.cc b/tests/ceftests/response_unittest.cc new file mode 100644 index 000000000..6d5544dcd --- /dev/null +++ b/tests/ceftests/response_unittest.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "include/cef_response.h" +#include "tests/ceftests/test_util.h" +#include "tests/gtest/include/gtest/gtest.h" + +TEST(ResponseTest, SetGetHeaderByName) { + CefRefPtr response(CefResponse::Create()); + EXPECT_TRUE(response.get() != NULL); + + CefResponse::HeaderMap headers, expectedHeaders; + + response->SetHeaderByName("HeaderA", "ValueA", false); + response->SetHeaderByName("HeaderB", "ValueB", false); + + expectedHeaders.insert(std::make_pair("HeaderA", "ValueA")); + expectedHeaders.insert(std::make_pair("HeaderB", "ValueB")); + + // Case insensitive retrieval. + EXPECT_STREQ("ValueA", + response->GetHeaderByName("headera").ToString().c_str()); + EXPECT_STREQ("ValueB", + response->GetHeaderByName("headerb").ToString().c_str()); + EXPECT_STREQ("", response->GetHeaderByName("noexist").ToString().c_str()); + + response->GetHeaderMap(headers); + TestMapEqual(expectedHeaders, headers, false); + + // Replace an existing value. + response->SetHeaderByName("HeaderA", "ValueANew", true); + + expectedHeaders.clear(); + expectedHeaders.insert(std::make_pair("HeaderA", "ValueANew")); + expectedHeaders.insert(std::make_pair("HeaderB", "ValueB")); + + // Case insensitive retrieval. + EXPECT_STREQ("ValueANew", + response->GetHeaderByName("headerA").ToString().c_str()); + + response->GetHeaderMap(headers); + TestMapEqual(expectedHeaders, headers, false); + + // Header with multiple values. + expectedHeaders.clear(); + expectedHeaders.insert(std::make_pair("HeaderA", "ValueA1")); + expectedHeaders.insert(std::make_pair("HeaderA", "ValueA2")); + expectedHeaders.insert(std::make_pair("HeaderB", "ValueB")); + response->SetHeaderMap(expectedHeaders); + + // When there are multiple values only the first is returned. + EXPECT_STREQ("ValueA1", + response->GetHeaderByName("headera").ToString().c_str()); + + // Don't overwrite the value. + response->SetHeaderByName("HeaderA", "ValueANew", false); + + response->GetHeaderMap(headers); + TestMapEqual(expectedHeaders, headers, false); + + // Overwrite the value (remove the duplicates). + response->SetHeaderByName("HeaderA", "ValueANew", true); + + expectedHeaders.clear(); + expectedHeaders.insert(std::make_pair("HeaderA", "ValueANew")); + expectedHeaders.insert(std::make_pair("HeaderB", "ValueB")); + + response->GetHeaderMap(headers); + TestMapEqual(expectedHeaders, headers, false); +}