drivefs_http_client_unittest.cc 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/components/drivefs/drivefs_http_client.h"
  5. #include <initializer_list>
  6. #include <utility>
  7. #include "ash/components/drivefs/mojom/drivefs.mojom-shared.h"
  8. #include "ash/components/drivefs/mojom/drivefs.mojom-test-utils.h"
  9. #include "ash/components/drivefs/mojom/drivefs.mojom.h"
  10. #include "base/strings/stringprintf.h"
  11. #include "base/test/bind.h"
  12. #include "base/test/gmock_callback_support.h"
  13. #include "base/test/gmock_move_support.h"
  14. #include "base/test/task_environment.h"
  15. #include "mojo/public/cpp/system/data_pipe.h"
  16. #include "mojo/public/cpp/system/data_pipe_utils.h"
  17. #include "net/http/http_status_code.h"
  18. #include "net/http/http_util.h"
  19. #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
  20. #include "services/network/public/mojom/data_pipe_getter.mojom.h"
  21. #include "services/network/public/mojom/url_response_head.mojom.h"
  22. #include "services/network/test/test_url_loader_factory.h"
  23. #include "testing/gmock/include/gmock/gmock.h"
  24. #include "testing/gtest/include/gtest/gtest.h"
  25. namespace drivefs {
  26. namespace mojom {
  27. // This HttpResponsePtr ostream printer pretty prints the response in test
  28. // failures.
  29. std::ostream& operator<<(std::ostream& os, const HttpResponsePtr& response) {
  30. os << "response_code: " << response->response_code << std::endl;
  31. os << "headers: " << std::endl;
  32. for (const auto& header : response->headers) {
  33. os << " " << header->key << ": " << header->value << std::endl;
  34. }
  35. return os;
  36. }
  37. } // namespace mojom
  38. namespace {
  39. using testing::_;
  40. using testing::Eq;
  41. using testing::IsEmpty;
  42. using testing::NiceMock;
  43. using testing::Pointee;
  44. using HttpHeaders = std::vector<mojom::HttpHeaderPtr>;
  45. using HttpHeadersInitializerList =
  46. std::initializer_list<std::pair<base::StringPiece, base::StringPiece>>;
  47. class MockHttpDelegate : public mojom::HttpDelegate {
  48. public:
  49. MOCK_METHOD(void,
  50. GetRequestBody,
  51. (mojo::ScopedDataPipeProducerHandle),
  52. (override));
  53. MOCK_METHOD(void, OnReceiveResponse, (mojom::HttpResponsePtr), (override));
  54. MOCK_METHOD(void,
  55. OnReceiveBody,
  56. (mojo::ScopedDataPipeConsumerHandle),
  57. (override));
  58. MOCK_METHOD(void,
  59. OnRequestComplete,
  60. (mojom::HttpCompletionStatusPtr),
  61. (override));
  62. };
  63. class DriveFsHttpClientTest : public testing::Test {
  64. public:
  65. DriveFsHttpClientTest()
  66. : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
  67. http_client_(
  68. base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
  69. &test_url_loader_factory_)) {}
  70. void BlockingCopyToString(std::string* result,
  71. mojo::ScopedDataPipeConsumerHandle source) {
  72. EXPECT_TRUE(mojo::BlockingCopyToString(std::move(source), result));
  73. }
  74. protected:
  75. base::test::TaskEnvironment task_environment_;
  76. scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
  77. network::TestURLLoaderFactory test_url_loader_factory_;
  78. DriveFsHttpClient http_client_;
  79. };
  80. struct ConsumeAllDataAndCompareWith {
  81. void operator()(mojo::ScopedDataPipeConsumerHandle handle) {
  82. std::string data;
  83. EXPECT_TRUE(mojo::BlockingCopyToString(std::move(handle), &data));
  84. EXPECT_EQ(data, expected_data);
  85. }
  86. base::StringPiece expected_data;
  87. };
  88. struct ProduceAllData {
  89. void operator()(mojo::ScopedDataPipeProducerHandle handle) {
  90. EXPECT_TRUE(mojo::BlockingCopyFromString(std::string(data), handle));
  91. }
  92. base::StringPiece data;
  93. };
  94. HttpHeaders HttpHeadersFromInitializerList(
  95. HttpHeadersInitializerList input_headers) {
  96. HttpHeaders headers;
  97. for (const auto& header : input_headers) {
  98. headers.push_back(mojom::HttpHeader::New(std::string{header.first},
  99. std::string{header.second}));
  100. }
  101. return headers;
  102. }
  103. network::mojom::URLResponseHeadPtr CreateURLResponseHead(
  104. net::HttpStatusCode http_status,
  105. HttpHeadersInitializerList input_headers) {
  106. auto head = network::mojom::URLResponseHead::New();
  107. const std::string status_line(
  108. base::StringPrintf("HTTP/1.1 %d %s", static_cast<int>(http_status),
  109. net::GetHttpReasonPhrase(http_status)));
  110. head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
  111. net::HttpUtil::AssembleRawHeaders(status_line));
  112. for (const auto& header : input_headers) {
  113. head->headers->AddHeader(header.first, header.second);
  114. }
  115. return head;
  116. }
  117. TEST_F(DriveFsHttpClientTest, RequestHandlesRequestData) {
  118. constexpr char kTestURL[] = "https://drive.google.com";
  119. constexpr base::StringPiece kTestRequestData = "Test Request Data";
  120. const HttpHeadersInitializerList kTestRequestHeaders = {
  121. {"Test-Request-Header", "Test-Value"},
  122. };
  123. base::RunLoop run_loop;
  124. NiceMock<MockHttpDelegate> http_delegate;
  125. mojom::HttpCompletionStatus expected_status(mojom::NetError::kOk, 0);
  126. EXPECT_CALL(http_delegate,
  127. OnRequestComplete(Pointee(Eq(std::ref(expected_status)))))
  128. .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
  129. EXPECT_CALL(http_delegate, GetRequestBody)
  130. .WillOnce(ProduceAllData{kTestRequestData});
  131. mojo::Receiver<mojom::HttpDelegate> http_receiver(&http_delegate);
  132. mojo::ScopedDataPipeProducerHandle producer;
  133. mojo::ScopedDataPipeConsumerHandle consumer;
  134. ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer, consumer), MOJO_RESULT_OK);
  135. test_url_loader_factory_.SetInterceptor(
  136. base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
  137. const std::vector<network::DataElement>* elements =
  138. request.request_body->elements();
  139. ASSERT_EQ(elements->size(), 1UL);
  140. const network::DataElement& element = elements->front();
  141. mojo::Remote<network::mojom::DataPipeGetter> remote(
  142. element.As<network::DataElementDataPipe>().CloneDataPipeGetter());
  143. remote->Read(std::move(producer),
  144. base::BindOnce([](int32_t, uint64_t) {}));
  145. }));
  146. http_client_.ExecuteHttpRequest(
  147. mojom::HttpRequest::New(
  148. /*method=*/"GET",
  149. /*url=*/kTestURL,
  150. /*headers=*/HttpHeadersFromInitializerList(kTestRequestHeaders),
  151. /*request_body_bytes=*/kTestRequestData.size()),
  152. http_receiver.BindNewPipeAndPassRemote());
  153. EXPECT_TRUE(
  154. test_url_loader_factory_.SimulateResponseForPendingRequest(kTestURL, ""));
  155. run_loop.Run();
  156. EXPECT_EQ(test_url_loader_factory_.NumPending(), 0);
  157. std::string data;
  158. EXPECT_TRUE(mojo::BlockingCopyToString(std::move(consumer), &data));
  159. EXPECT_EQ(data, kTestRequestData);
  160. }
  161. TEST_F(DriveFsHttpClientTest, RequestHandlesResponseData) {
  162. constexpr char kTestURL[] = "https://drive.google.com";
  163. constexpr base::StringPiece kTestResponseData = "Test Response Data";
  164. const HttpHeadersInitializerList kTestResponseHeaders = {
  165. {"Test-Response-Header", "Test-Value"},
  166. };
  167. base::RunLoop run_loop;
  168. NiceMock<MockHttpDelegate> http_delegate;
  169. mojom::HttpCompletionStatus expected_status(mojom::NetError::kOk,
  170. kTestResponseData.size());
  171. EXPECT_CALL(http_delegate,
  172. OnRequestComplete(Pointee(Eq(std::ref(expected_status)))))
  173. .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
  174. mojom::HttpResponse expected_response(
  175. net::HTTP_OK, HttpHeadersFromInitializerList(kTestResponseHeaders));
  176. EXPECT_CALL(http_delegate,
  177. OnReceiveResponse(Pointee(Eq(std::ref(expected_response)))));
  178. EXPECT_CALL(http_delegate, OnReceiveBody)
  179. .WillOnce(ConsumeAllDataAndCompareWith{kTestResponseData});
  180. mojo::Receiver<mojom::HttpDelegate> http_receiver(&http_delegate);
  181. http_client_.ExecuteHttpRequest(mojom::HttpRequest::New(
  182. /*method=*/"GET",
  183. /*url=*/kTestURL,
  184. /*headers=*/HttpHeaders(),
  185. /*request_body_bytes=*/0),
  186. http_receiver.BindNewPipeAndPassRemote());
  187. EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest(
  188. GURL(kTestURL), network::URLLoaderCompletionStatus(net::OK),
  189. CreateURLResponseHead(net::HTTP_OK, kTestResponseHeaders),
  190. std::string(kTestResponseData)));
  191. run_loop.Run();
  192. EXPECT_EQ(test_url_loader_factory_.NumPending(), 0);
  193. }
  194. TEST_F(DriveFsHttpClientTest, RequestCancelledOnRedirect) {
  195. constexpr char kTestURL[] = "https://drive.google.com";
  196. // Populate the URLLoaderFactory with a redirect.
  197. net::RedirectInfo redirect;
  198. redirect.status_code = 301;
  199. redirect.new_url = GURL("https://not_a_virus.google.com/");
  200. network::TestURLLoaderFactory::Redirects redirects;
  201. redirects.push_back({redirect, network::mojom::URLResponseHead::New()});
  202. test_url_loader_factory_.AddResponse(
  203. GURL(kTestURL), network::mojom::URLResponseHead::New(),
  204. /*content=*/"", network::URLLoaderCompletionStatus(),
  205. std::move(redirects),
  206. network::TestURLLoaderFactory::kResponseOnlyRedirectsNoDestination);
  207. // Run until the delegate is disconnected, there should be no callbacks.
  208. base::RunLoop run_loop;
  209. NiceMock<MockHttpDelegate> http_delegate;
  210. EXPECT_CALL(http_delegate, OnReceiveResponse).Times(0);
  211. EXPECT_CALL(http_delegate, OnRequestComplete).Times(0);
  212. mojo::Receiver<mojom::HttpDelegate> http_receiver(&http_delegate);
  213. mojo::PendingRemote<mojom::HttpDelegate> http_remote =
  214. http_receiver.BindNewPipeAndPassRemote();
  215. http_receiver.set_disconnect_handler(run_loop.QuitClosure());
  216. http_client_.ExecuteHttpRequest(mojom::HttpRequest::New(
  217. /*method=*/"GET",
  218. /*url=*/kTestURL,
  219. /*headers=*/HttpHeaders(),
  220. /*request_body_bytes=*/0),
  221. std::move(http_remote));
  222. run_loop.Run();
  223. EXPECT_EQ(test_url_loader_factory_.NumPending(), 0);
  224. }
  225. } // namespace
  226. } // namespace drivefs