Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(856)

Unified Diff: mojo/services/network/http_connection_impl.cc

Issue 1128863004: Mojo service implementation for HTTP server - part 2 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « mojo/services/network/http_connection_impl.h ('k') | mojo/services/network/http_server_apptest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: mojo/services/network/http_connection_impl.cc
diff --git a/mojo/services/network/http_connection_impl.cc b/mojo/services/network/http_connection_impl.cc
index 47449ecd557894cf67ea108407706f7a8e78990f..24fc7c5de90c326d88650910fba7aa99854bb8ae 100644
--- a/mojo/services/network/http_connection_impl.cc
+++ b/mojo/services/network/http_connection_impl.cc
@@ -6,14 +6,113 @@
#include <limits>
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "mojo/common/handle_watcher.h"
#include "mojo/services/network/http_server_impl.h"
#include "mojo/services/network/net_adapters.h"
#include "net/base/net_errors.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_status_code.h"
#include "net/server/http_server.h"
#include "net/server/http_server_request_info.h"
+#include "net/server/http_server_response_info.h"
+#include "third_party/mojo/src/mojo/public/cpp/bindings/type_converter.h"
+#include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h"
namespace mojo {
+// SimpleDataPipeReader reads till end-of-file, stores the data in a string and
+// notifies completion.
+class HttpConnectionImpl::SimpleDataPipeReader {
+ public:
+ using CompletionCallback =
+ base::Callback<void(SimpleDataPipeReader*, scoped_ptr<std::string>)>;
+
+ SimpleDataPipeReader() {}
+ ~SimpleDataPipeReader() {}
+
+ void Start(ScopedDataPipeConsumerHandle consumer,
+ const CompletionCallback& completion_callback) {
+ DCHECK(consumer.is_valid() && !consumer_.is_valid());
+ consumer_ = consumer.Pass();
+ completion_callback_ = completion_callback;
+ buffer_.reset(new std::string);
+ ReadMore();
+ }
+
+ private:
+ void ReadMore() {
+ const void* buf;
+ uint32_t buf_size;
+ MojoResult rv = BeginReadDataRaw(consumer_.get(), &buf, &buf_size,
+ MOJO_READ_DATA_FLAG_NONE);
+ if (rv == MOJO_RESULT_OK) {
+ buffer_->append(static_cast<const char*>(buf), buf_size);
+ EndReadDataRaw(consumer_.get(), buf_size);
+ WaitToReadMore();
+ } else if (rv == MOJO_RESULT_SHOULD_WAIT) {
+ WaitToReadMore();
+ } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+ // We reached end-of-file.
+ completion_callback_.Run(this, buffer_.Pass());
+ // Note: This object may have been destroyed in the callback.
+ } else {
+ CHECK(false);
+ }
+ }
+
+ void WaitToReadMore() {
+ watcher_.Start(consumer_.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&SimpleDataPipeReader::OnHandleReady,
+ base::Unretained(this)));
+ }
+
+ void OnHandleReady(MojoResult result) { ReadMore(); }
+
+ ScopedDataPipeConsumerHandle consumer_;
+ common::HandleWatcher watcher_;
+ CompletionCallback completion_callback_;
+ scoped_ptr<std::string> buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleDataPipeReader);
+};
+
+template <>
+struct TypeConverter<URLRequestPtr, net::HttpServerRequestInfo> {
+ static URLRequestPtr Convert(const net::HttpServerRequestInfo& obj) {
+ URLRequestPtr request(URLRequest::New());
+ request->url = obj.path;
+ request->method = obj.method;
+ request->headers.resize(obj.headers.size());
+ size_t index = 0;
+ for (const auto& item : obj.headers) {
+ HTTPHeaderPtr header(HTTPHeader::New());
+ header->name = item.first;
+ header->value = item.second;
+ request->headers[index++] = header.Pass();
+ }
+ if (!obj.data.empty()) {
+ uint32_t num_bytes = static_cast<uint32_t>(obj.data.size());
+ MojoCreateDataPipeOptions options;
+ options.struct_size = sizeof(MojoCreateDataPipeOptions);
+ options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+ options.element_num_bytes = 1;
+ options.capacity_num_bytes = num_bytes;
+ DataPipe data_pipe(options);
+ request->body.push_back(data_pipe.consumer_handle.Pass());
+ MojoResult result =
+ WriteDataRaw(data_pipe.producer_handle.get(), obj.data.data(),
+ &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
+ DCHECK_EQ(MOJO_RESULT_OK, result);
jam 2015/05/15 06:46:43 nit: perhaps CHECK so that we know if we have to h
yzshen1 2015/05/15 17:22:54 Done.
+ }
+ return request.Pass();
+ }
+};
+
HttpConnectionImpl::HttpConnectionImpl(int connection_id,
HttpServerImpl* owner,
HttpConnectionDelegatePtr delegate,
@@ -21,16 +120,36 @@ HttpConnectionImpl::HttpConnectionImpl(int connection_id,
: connection_id_(connection_id),
owner_(owner),
delegate_(delegate.Pass()),
- binding_(this, connection) {
+ binding_(this, connection),
+ encountered_connection_error_(false) {
+
binding_.set_error_handler(this);
delegate_.set_error_handler(this);
}
-HttpConnectionImpl::~HttpConnectionImpl() {}
+HttpConnectionImpl::~HttpConnectionImpl() {
+ for (const auto& reader : response_body_readers_)
jam 2015/05/15 06:46:43 nit: STLDeleteElements
yzshen1 2015/05/15 17:22:54 Done.
+ delete reader;
+}
void HttpConnectionImpl::OnReceivedHttpRequest(
const net::HttpServerRequestInfo& info) {
- // TODO(yzshen): implement it.
+ if (!delegate_)
+ return;
+
+ delegate_->OnReceivedRequest(
+ URLRequest::From(info), [this](URLResponsePtr response) {
+ if (response->body.is_valid()) {
+ SimpleDataPipeReader* reader = new SimpleDataPipeReader;
+ response_body_readers_.insert(reader);
+ reader->Start(
+ response->body.Pass(),
+ base::Bind(&HttpConnectionImpl::OnFinishedReadingResponseBody,
+ base::Unretained(this), base::Passed(&response)));
+ } else {
+ OnFinishedReadingResponseBody(response.Pass(), nullptr, nullptr);
+ }
+ });
}
void HttpConnectionImpl::OnReceivedWebSocketRequest(
@@ -65,9 +184,60 @@ void HttpConnectionImpl::SetReceiveBufferSize(
}
void HttpConnectionImpl::OnConnectionError() {
- // The proxy side of |binding_| or the impl side of |delegate_| has closed the
- // pipe. The connection is not needed anymore.
- owner_->server()->Close(connection_id_);
+ if (!encountered_connection_error_) {
jam 2015/05/15 06:46:43 why is this boolean needed? perhaps document why i
yzshen1 2015/05/15 17:22:54 I realized that based on the current code it won't
+ // The proxy side of |binding_| or the impl side of |delegate_| has closed
+ // the pipe.
+ encountered_connection_error_ = true;
+ binding_.Close();
+ delegate_.reset();
+
+ // Don't close the connection until all pending responses are sent.
+ if (response_body_readers_.empty())
+ owner_->server()->Close(connection_id_);
+ }
+}
+
+void HttpConnectionImpl::OnFinishedReadingResponseBody(
+ URLResponsePtr response,
+ SimpleDataPipeReader* reader,
+ scoped_ptr<std::string> body) {
+ if (reader) {
+ delete reader;
+ response_body_readers_.erase(reader);
+ }
+
+ net::HttpServerResponseInfo info(
+ static_cast<net::HttpStatusCode>(response->status_code));
+
+ std::string content_type;
+ for (size_t i = 0; i < response->headers.size(); ++i) {
+ const HTTPHeader& header = *(response->headers[i]);
+
+ if (body) {
+ // net::HttpServerResponseInfo::SetBody() automatically sets
+ // Content-Length and Content-Types, so skip the two here.
+ //
+ // TODO(yzshen): Consider adding to net::HttpServerResponseInfo a simple
+ // setter for body which doesn't fiddle with headers.
+ if (base::strcasecmp(header.name.data(),
+ net::HttpRequestHeaders::kContentLength) == 0) {
+ continue;
+ } else if (base::strcasecmp(header.name.data(),
+ net::HttpRequestHeaders::kContentType) == 0) {
+ content_type = header.value;
+ continue;
+ }
+ }
+ info.AddHeader(header.name, header.value);
+ }
+
+ if (body)
+ info.SetBody(*body, content_type);
+
+ owner_->server()->SendResponse(connection_id_, info);
+
+ if (response_body_readers_.empty() && encountered_connection_error_)
+ owner_->server()->Close(connection_id_);
}
} // namespace mojo
« no previous file with comments | « mojo/services/network/http_connection_impl.h ('k') | mojo/services/network/http_server_apptest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698