// 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 "mojo/public/cpp/bindings/lib/router.h"
#include "mojo/public/cpp/environment/logging.h"
namespace mojo {
namespace internal {
// ----------------------------------------------------------------------------
class ResponderThunk : public MessageReceiver {
public:
explicit ResponderThunk(const SharedData<Router*>& router)
: router_(router) {
}
virtual ~ResponderThunk() {
}
// MessageReceiver implementation:
virtual bool Accept(Message* message) MOJO_OVERRIDE {
MOJO_DCHECK(message->has_flag(kMessageIsResponse));
bool result = false;
Router* router = router_.value();
if (router)
result = router->Accept(message);
return result;
}
private:
SharedData<Router*> router_;
};
// ----------------------------------------------------------------------------
Router::HandleIncomingMessageThunk::HandleIncomingMessageThunk(Router* router)
: router_(router) {
}
Router::HandleIncomingMessageThunk::~HandleIncomingMessageThunk() {
}
bool Router::HandleIncomingMessageThunk::Accept(Message* message) {
return router_->HandleIncomingMessage(message);
}
// ----------------------------------------------------------------------------
Router::Router(ScopedMessagePipeHandle message_pipe,
FilterChain filters,
const MojoAsyncWaiter* waiter)
: thunk_(this),
filters_(filters.Pass()),
connector_(message_pipe.Pass(), waiter),
weak_self_(this),
incoming_receiver_(NULL),
next_request_id_(0),
testing_mode_(false) {
filters_.SetSink(&thunk_);
connector_.set_incoming_receiver(filters_.GetHead());
}
Router::~Router() {
weak_self_.set_value(NULL);
for (ResponderMap::const_iterator i = responders_.begin();
i != responders_.end(); ++i) {
delete i->second;
}
}
bool Router::Accept(Message* message) {
MOJO_DCHECK(!message->has_flag(kMessageExpectsResponse));
return connector_.Accept(message);
}
bool Router::AcceptWithResponder(Message* message,
MessageReceiver* responder) {
MOJO_DCHECK(message->has_flag(kMessageExpectsResponse));
// Reserve 0 in case we want it to convey special meaning in the future.
uint64_t request_id = next_request_id_++;
if (request_id == 0)
request_id = next_request_id_++;
message->set_request_id(request_id);
if (!connector_.Accept(message))
return false;
// We assume ownership of |responder|.
responders_[request_id] = responder;
return true;
}
void Router::EnableTestingMode() {
testing_mode_ = true;
connector_.set_enforce_errors_from_incoming_receiver(false);
}
bool Router::HandleIncomingMessage(Message* message) {
if (message->has_flag(kMessageExpectsResponse)) {
if (incoming_receiver_) {
MessageReceiver* responder = new ResponderThunk(weak_self_);
bool ok = incoming_receiver_->AcceptWithResponder(message, responder);
if (!ok)
delete responder;
return ok;
}
// If we receive a request expecting a response when the client is not
// listening, then we have no choice but to tear down the pipe.
connector_.CloseMessagePipe();
} else if (message->has_flag(kMessageIsResponse)) {
uint64_t request_id = message->request_id();
ResponderMap::iterator it = responders_.find(request_id);
if (it == responders_.end()) {
MOJO_DCHECK(testing_mode_);
return false;
}
MessageReceiver* responder = it->second;
responders_.erase(it);
bool ok = responder->Accept(message);
delete responder;
return ok;
} else {
if (incoming_receiver_)
return incoming_receiver_->Accept(message);
// OK to drop message on the floor.
}
return false;
}
// ----------------------------------------------------------------------------
} // namespace internal
} // namespace mojo