blob: 6b60492d3d5d3063c4cffe304122e4396033a91a [file] [log] [blame]
// Copyright 2014 The Chromium 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 <stdio.h>
#if defined(OS_WIN)
#include <winsock2.h>
#elif defined(OS_POSIX)
#include <arpa/inet.h>
#endif
#include <algorithm>
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "mojo/common/weak_binding_set.h"
#include "mojo/public/c/system/main.h"
#include "mojo/public/cpp/application/application_delegate.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "mojo/public/cpp/application/application_runner.h"
#include "mojo/public/cpp/application/interface_factory.h"
#include "mojo/public/cpp/bindings/error_handler.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "mojo/services/network/public/interfaces/network_service.mojom.h"
#include "services/http_server/connection.h"
#include "services/http_server/public/http_request.mojom.h"
#include "services/http_server/public/http_response.mojom.h"
#include "services/http_server/public/http_server.mojom.h"
#include "services/http_server/public/http_server_util.h"
#include "third_party/re2/re2/re2.h"
using mojo::ScopedDataPipeConsumerHandle;
using mojo::ScopedDataPipeProducerHandle;
using mojo::TCPConnectedSocketPtr;
namespace http_server {
class HttpServerApp : public HttpServer,
public mojo::ApplicationDelegate,
public mojo::ErrorHandler,
public mojo::InterfaceFactory<HttpServer> {
public:
HttpServerApp() : weak_ptr_factory_(this) {}
virtual void Initialize(mojo::ApplicationImpl* app) override {
app->ConnectToService("mojo:network_service", &network_service_);
Start();
}
// HttpServer
void SetHandler(const mojo::String& path,
HttpHandlerPtr http_handler,
const mojo::Callback<void(bool)>& callback) override {
for (const auto& handler : handlers_) {
if (handler->pattern->pattern() == path)
callback.Run(false);
}
http_handler.set_error_handler(this);
handlers_.push_back(new Handler(path, http_handler.Pass()));
callback.Run(true);
}
private:
// ErrorHandler:
void OnConnectionError() override {
handlers_.erase(
std::remove_if(handlers_.begin(),
handlers_.end(),
[](Handler* h) {
return h->http_handler.encountered_error();
}));
}
// ApplicationDelegate:
bool ConfigureIncomingConnection(
mojo::ApplicationConnection* connection) override {
connection->AddService(this);
return true;
}
// InterfaceFactory<HttpServerService>:
void Create(mojo::ApplicationConnection* connection,
mojo::InterfaceRequest<HttpServer> request) override {
http_server_bindings_.AddBinding(this, request.Pass());
}
void OnSocketBound(mojo::NetworkErrorPtr err,
mojo::NetAddressPtr bound_address) {
if (err->code != 0) {
printf("Bound err = %d\n", err->code);
return;
}
printf("Got address %d.%d.%d.%d:%d\n",
(int)bound_address->ipv4->addr[0],
(int)bound_address->ipv4->addr[1],
(int)bound_address->ipv4->addr[2],
(int)bound_address->ipv4->addr[3],
(int)bound_address->ipv4->port);
}
void OnSocketListening(mojo::NetworkErrorPtr err) {
if (err->code != 0) {
printf("Listen err = %d\n", err->code);
return;
}
printf("Waiting for incoming connections...\n");
}
void OnConnectionAccepted(mojo::NetworkErrorPtr err,
mojo::NetAddressPtr remote_address) {
if (err->code != 0) {
printf("Accepted socket error = %d\n", err->code);
return;
}
new Connection(pending_connected_socket_.Pass(),
pending_send_handle_.Pass(),
pending_receive_handle_.Pass(),
base::Bind(&HttpServerApp::HandleRequest,
weak_ptr_factory_.GetWeakPtr()));
// Ready for another connection.
WaitForNextConnection();
}
void WaitForNextConnection() {
// Need two pipes (one for each direction).
ScopedDataPipeConsumerHandle send_consumer_handle;
MojoResult result = CreateDataPipe(
nullptr, &pending_send_handle_, &send_consumer_handle);
assert(result == MOJO_RESULT_OK);
mojo::ScopedDataPipeProducerHandle receive_producer_handle;
result = CreateDataPipe(
nullptr, &receive_producer_handle, &pending_receive_handle_);
assert(result == MOJO_RESULT_OK);
MOJO_ALLOW_UNUSED_LOCAL(result);
server_socket_->Accept(send_consumer_handle.Pass(),
receive_producer_handle.Pass(),
GetProxy(&pending_connected_socket_),
base::Bind(&HttpServerApp::OnConnectionAccepted,
base::Unretained(this)));
}
void Start() {
mojo::NetAddressPtr net_address(mojo::NetAddress::New());
net_address->family = mojo::NET_ADDRESS_FAMILY_IPV4;
net_address->ipv4 = mojo::NetAddressIPv4::New();
net_address->ipv4->addr.resize(4);
net_address->ipv4->addr[0] = 0;
net_address->ipv4->addr[1] = 0;
net_address->ipv4->addr[2] = 0;
net_address->ipv4->addr[3] = 0;
net_address->ipv4->port = 80;
// Note that we can start using the proxies right away even thought the
// callbacks have not been called yet. If a previous step fails, they'll
// all fail.
network_service_->CreateTCPBoundSocket(
net_address.Pass(),
GetProxy(&bound_socket_),
base::Bind(&HttpServerApp::OnSocketBound, base::Unretained(this)));
bound_socket_->StartListening(GetProxy(
&server_socket_),
base::Bind(&HttpServerApp::OnSocketListening, base::Unretained(this)));
WaitForNextConnection();
}
void HandleRequest(Connection* connection, HttpRequestPtr request) {
for (auto& handler : handlers_) {
if (RE2::FullMatch(request->relative_url.data(), *handler->pattern)) {
handler->http_handler->HandleRequest(
request.Pass(),
base::Bind(&HttpServerApp::OnResponse,
base::Unretained(this),
connection));
return;
}
}
connection->SendResponse(
CreateHttpResponse(404, "No registered handler\n"));
}
void OnResponse(Connection* connection, HttpResponsePtr response) {
connection->SendResponse(response.Pass());
}
struct Handler {
Handler(const std::string& pattern,
HttpHandlerPtr http_handler)
: pattern(new RE2(pattern.c_str())), http_handler(http_handler.Pass()) {
}
scoped_ptr<RE2> pattern;
HttpHandlerPtr http_handler;
private:
DISALLOW_COPY_AND_ASSIGN(Handler);
};
base::WeakPtrFactory<HttpServerApp> weak_ptr_factory_;
mojo::WeakBindingSet<HttpServer> http_server_bindings_;
mojo::NetworkServicePtr network_service_;
mojo::TCPBoundSocketPtr bound_socket_;
mojo::TCPServerSocketPtr server_socket_;
ScopedDataPipeProducerHandle pending_send_handle_;
ScopedDataPipeConsumerHandle pending_receive_handle_;
TCPConnectedSocketPtr pending_connected_socket_;
ScopedVector<Handler> handlers_;
};
} // namespace http_server
MojoResult MojoMain(MojoHandle shell_handle) {
mojo::ApplicationRunner runner(new http_server::HttpServerApp);
return runner.Run(shell_handle);
}