| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/test/embedded_test_server/http1_connection.h" |
| |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/strings/stringprintf.h" |
| #include "net/base/completion_once_callback.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/test_completion_callback.h" |
| #include "net/socket/stream_socket.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| |
| namespace net::test_server { |
| |
| Http1Connection::Http1Connection( |
| std::unique_ptr<StreamSocket> socket, |
| EmbeddedTestServerConnectionListener* connection_listener, |
| EmbeddedTestServer* server_delegate) |
| : socket_(std::move(socket)), |
| connection_listener_(connection_listener), |
| server_delegate_(server_delegate), |
| read_buf_(base::MakeRefCounted<IOBufferWithSize>(4096)) {} |
| |
| Http1Connection::~Http1Connection() { |
| weak_factory_.InvalidateWeakPtrs(); |
| } |
| |
| void Http1Connection::OnSocketReady() { |
| ReadData(); |
| } |
| |
| std::unique_ptr<StreamSocket> Http1Connection::TakeSocket() { |
| return std::move(socket_); |
| } |
| |
| StreamSocket* Http1Connection::Socket() { |
| return socket_.get(); |
| } |
| |
| base::WeakPtr<HttpConnection> Http1Connection::GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| void Http1Connection::ReadData() { |
| while (true) { |
| int rv = socket_->Read(read_buf_.get(), read_buf_->size(), |
| base::BindOnce(&Http1Connection::OnReadCompleted, |
| weak_factory_.GetWeakPtr())); |
| if (rv == ERR_IO_PENDING) |
| return; |
| |
| if (HandleReadResult(rv)) { |
| return; |
| } |
| } |
| } |
| |
| void Http1Connection::OnReadCompleted(int rv) { |
| if (!HandleReadResult(rv)) |
| ReadData(); |
| } |
| |
| bool Http1Connection::HandleReadResult(int rv) { |
| if (rv <= 0) { |
| server_delegate_->RemoveConnection(this); |
| return true; |
| } |
| |
| if (connection_listener_) |
| connection_listener_->ReadFromSocket(*socket_, rv); |
| |
| request_parser_.ProcessChunk(base::StringPiece(read_buf_->data(), rv)); |
| if (request_parser_.ParseRequest() != HttpRequestParser::ACCEPTED) |
| return false; |
| |
| std::unique_ptr<HttpRequest> request = request_parser_.GetRequest(); |
| |
| SSLInfo ssl_info; |
| if (socket_->GetSSLInfo(&ssl_info)) |
| request->ssl_info = ssl_info; |
| |
| server_delegate_->HandleRequest(weak_factory_.GetWeakPtr(), |
| std::move(request)); |
| return true; |
| } |
| |
| void Http1Connection::AddResponse(std::unique_ptr<HttpResponse> response) { |
| responses_.push_back(std::move(response)); |
| } |
| |
| void Http1Connection::SendResponseHeaders(HttpStatusCode status, |
| const std::string& status_reason, |
| const base::StringPairs& headers) { |
| std::string response_builder; |
| |
| base::StringAppendF(&response_builder, "HTTP/1.1 %d %s\r\n", status, |
| status_reason.c_str()); |
| for (const auto& header_pair : headers) { |
| const std::string& header_name = header_pair.first; |
| const std::string& header_value = header_pair.second; |
| base::StringAppendF(&response_builder, "%s: %s\r\n", header_name.c_str(), |
| header_value.c_str()); |
| } |
| |
| base::StringAppendF(&response_builder, "\r\n"); |
| SendRawResponseHeaders(response_builder); |
| } |
| |
| void Http1Connection::SendRawResponseHeaders(const std::string& headers) { |
| SendContents(headers, base::DoNothing()); |
| } |
| |
| void Http1Connection::SendContents(const std::string& contents, |
| base::OnceClosure callback) { |
| if (contents.empty()) { |
| std::move(callback).Run(); |
| return; |
| } |
| |
| scoped_refptr<DrainableIOBuffer> buf = |
| base::MakeRefCounted<DrainableIOBuffer>( |
| base::MakeRefCounted<StringIOBuffer>(contents), contents.length()); |
| |
| SendInternal(std::move(callback), buf); |
| } |
| |
| void Http1Connection::FinishResponse() { |
| server_delegate_->RemoveConnection(this, connection_listener_); |
| } |
| |
| void Http1Connection::SendContentsAndFinish(const std::string& contents) { |
| SendContents(contents, base::BindOnce(&HttpResponseDelegate::FinishResponse, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void Http1Connection::SendHeadersContentAndFinish( |
| HttpStatusCode status, |
| const std::string& status_reason, |
| const base::StringPairs& headers, |
| const std::string& contents) { |
| SendResponseHeaders(status, status_reason, headers); |
| SendContentsAndFinish(contents); |
| } |
| |
| void Http1Connection::SendInternal(base::OnceClosure callback, |
| scoped_refptr<DrainableIOBuffer> buf) { |
| while (buf->BytesRemaining() > 0) { |
| auto split_callback = base::SplitOnceCallback(std::move(callback)); |
| callback = std::move(split_callback.first); |
| int rv = |
| socket_->Write(buf.get(), buf->BytesRemaining(), |
| base::BindOnce(&Http1Connection::OnSendInternalDone, |
| base::Unretained(this), |
| std::move(split_callback.second), buf), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| if (rv == ERR_IO_PENDING) |
| return; |
| |
| if (rv < 0) |
| break; |
| buf->DidConsume(rv); |
| } |
| |
| // The Http1Connection will be deleted by the callback since we only need |
| // to serve a single request. |
| std::move(callback).Run(); |
| } |
| |
| void Http1Connection::OnSendInternalDone(base::OnceClosure callback, |
| scoped_refptr<DrainableIOBuffer> buf, |
| int rv) { |
| if (rv < 0) { |
| std::move(callback).Run(); |
| return; |
| } |
| buf->DidConsume(rv); |
| SendInternal(std::move(callback), buf); |
| } |
| |
| } // namespace net::test_server |