| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ios/testing/embedded_test_server_handlers.h" |
| |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/strings/escape.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "url/gurl.h" |
| |
| namespace testing { |
| |
| const char kTestFormPage[] = "ios.testing.HandleForm"; |
| const char kTestFormFieldValue[] = "test-value"; |
| const char kTestDownloadMimeType[] = "application/vnd.test"; |
| |
| namespace { |
| // Extracts and escapes url spec from the query. |
| std::string ExtractUlrSpecFromQuery( |
| const net::test_server::HttpRequest& request) { |
| GURL request_url = request.GetURL(); |
| std::string spec = |
| base::UnescapeBinaryURLComponent(request_url.query_piece()); |
| |
| // Escape the URL spec. |
| GURL url(spec); |
| return url.is_valid() ? base::EscapeForHTML(url.spec()) : spec; |
| } |
| |
| // A HttpResponse that responds with |length| zeroes and kTestDownloadMimeType |
| // MIME Type. |
| class DownloadResponse : public net::test_server::BasicHttpResponse { |
| public: |
| DownloadResponse(int length) : length_(length) {} |
| |
| DownloadResponse(const DownloadResponse&) = delete; |
| DownloadResponse& operator=(const DownloadResponse&) = delete; |
| |
| void SendResponse( |
| base::WeakPtr<net::test_server::HttpResponseDelegate> delegate) override { |
| base::StringPairs headers = { |
| {"content-type", kTestDownloadMimeType}, |
| {"content-length", base::StringPrintf("%d", length_)}}; |
| |
| delegate->SendResponseHeaders(net::HTTP_OK, "OK", headers); |
| Send(delegate, length_); |
| } |
| |
| private: |
| // Sends "0" |count| times using 1KB blocks. Using blocks with smaller size is |
| // performance inefficient and can cause unnecessary delays especially when |
| // multiple tests run in parallel on a single machine. |
| static void Send( |
| base::WeakPtr<net::test_server::HttpResponseDelegate> delegate, |
| int count) { |
| if (!count) { |
| if (delegate) |
| delegate->FinishResponse(); |
| return; |
| } |
| int block_size = std::min(count, 1000); |
| std::string content_block(block_size, 0); |
| auto next_send = |
| base::BindOnce(&DownloadResponse::Send, delegate, count - block_size); |
| |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&net::test_server::HttpResponseDelegate::SendContents, |
| delegate, content_block, std::move(next_send)), |
| base::Milliseconds(100)); |
| } |
| |
| int length_ = 0; |
| }; |
| |
| // A slow response that would take several hours to finish. This is useful for |
| // testing scenarios where a load is interrupted after it starts but before it |
| // finishes. |
| class SlowResponse : public net::test_server::BasicHttpResponse { |
| public: |
| SlowResponse() = default; |
| |
| SlowResponse(const SlowResponse&) = delete; |
| SlowResponse& operator=(const SlowResponse&) = delete; |
| |
| void SendResponse( |
| base::WeakPtr<net::test_server::HttpResponseDelegate> delegate) override { |
| delegate_ = delegate; |
| delegate_->SendResponseHeaders( |
| net::HTTP_OK, "OK", |
| {{"Content-Length", |
| base::NumberToString(kilobytes_left_to_send_ * 1024)}, |
| {"Content-Type", "text/plain"}}); |
| SendKilobyte(); |
| } |
| |
| private: |
| void SendKilobyte() { |
| if (kilobytes_left_to_send_ == 0) { |
| delegate_->FinishResponse(); |
| return; |
| } |
| |
| DCHECK_GT(kilobytes_left_to_send_, 0); |
| kilobytes_left_to_send_--; |
| |
| delegate_->SendContents( |
| std::string(1024, 'a'), |
| base::BindOnce(&SlowResponse::SendKilobyteAfterDelay, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void SendKilobyteAfterDelay() { |
| base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&SlowResponse::SendKilobyte, weak_factory_.GetWeakPtr()), |
| delay_between_kilobytes_); |
| } |
| |
| base::WeakPtrFactory<SlowResponse> weak_factory_{this}; |
| base::WeakPtr<net::test_server::HttpResponseDelegate> delegate_ = nullptr; |
| int kilobytes_left_to_send_ = 100000; |
| base::TimeDelta delay_between_kilobytes_ = base::Seconds(1); |
| }; |
| |
| } // namespace |
| |
| std::unique_ptr<net::test_server::HttpResponse> HandleIFrame( |
| const net::test_server::HttpRequest& request) { |
| auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| http_response->set_content_type("text/html"); |
| http_response->set_content( |
| base::StringPrintf("<html><head></head><body><iframe " |
| "src='%s'></iframe>Main frame text</body></html>", |
| ExtractUlrSpecFromQuery(request).c_str())); |
| return std::move(http_response); |
| } |
| |
| // Returns a page with |html|. |
| std::unique_ptr<net::test_server::HttpResponse> HandlePageWithHtml( |
| const std::string& html, |
| const net::test_server::HttpRequest& request) { |
| auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| http_response->set_content_type("text/html"); |
| http_response->set_content(html); |
| return std::move(http_response); |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> HandlePageWithContents( |
| const net::test_server::HttpRequest& request) { |
| auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| http_response->set_content_type("text/html"); |
| http_response->set_content(request.GetURL().query()); |
| return std::move(http_response); |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> HandleEchoQueryOrCloseSocket( |
| const bool& responds_with_content, |
| const net::test_server::HttpRequest& request) { |
| if (!responds_with_content) { |
| return std::make_unique<net::test_server::RawHttpResponse>( |
| /*headers=*/"", /*contents=*/""); |
| } |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| response->set_content_type("text/html"); |
| response->set_content(request.GetURL().query()); |
| response->AddCustomHeader("Cache-Control", "no-store"); |
| return std::move(response); |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> HandleForm( |
| const net::test_server::HttpRequest& request) { |
| std::string form_action = ExtractUlrSpecFromQuery(request); |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| response->set_content_type("text/html"); |
| response->set_content(base::StringPrintf( |
| "<form method='post' id='form' action='%s'>" |
| " <input type='text' name='test-name' value='%s'>" |
| "</form>" |
| "%s", |
| form_action.c_str(), kTestFormFieldValue, kTestFormPage)); |
| |
| return std::move(response); |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> HandleDownload( |
| const net::test_server::HttpRequest& request) { |
| int length = 0; |
| if (!base::StringToInt(request.GetURL().query(), &length)) { |
| length = 1; |
| } |
| |
| return std::make_unique<DownloadResponse>(length); |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> HandleSlow( |
| const net::test_server::HttpRequest& request) { |
| return std::make_unique<SlowResponse>(); |
| } |
| |
| } // namespace testing |