/** * @param \Thruway\Event\MessageEvent $messageEvent */ public function handleMessage(MessageEvent $messageEvent) { if (!$this->isAuthorizedTo($messageEvent->session, $messageEvent->message)) { $messageEvent->session->sendMessage(ErrorMessage::createErrorMessageFromMessage($messageEvent->message, "wamp.error.not_authorized")); $messageEvent->stopPropagation(); } }
/** * handle received message * * @param \Thruway\AbstractSession $session * @param \Thruway\Message\Message $msg * @return void */ public function onMessage(AbstractSession $session, Message $msg) { if ($msg instanceof PublishedMessage) { $this->processPublished($msg); } elseif ($msg instanceof ErrorMessage) { $this->processError($msg); } else { $session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg)); } }
/** * @param Session $session * @param Message $msg */ public function onMessage(Session $session, Message $msg) { if (!$session->isAuthenticated()) { if ($msg instanceof HelloMessage) { $this->manager->debug("got hello"); // send welcome message if ($this->sessions->contains($session)) { $this->manager->error("Connection tried to rejoin realm when it is already joined to the realm."); $session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg)); // TODO should shut down session here } else { $this->sessions->attach($session); $session->setRealm($this); $session->setState(Session::STATE_UP); // this should probably be after authentication if ($this->getAuthenticationManager() !== null) { $this->getAuthenticationManager()->onAuthenticationMessage($this, $session, $msg); } else { $session->setAuthenticated(true); $session->setAuthenticationDetails(AuthenticationDetails::createAnonymous()); // the broker and dealer should give us this information $roles = array("broker" => new \stdClass(), "dealer" => new \stdClass()); $session->sendMessage(new WelcomeMessage($session->getSessionId(), array("roles" => $roles))); } } } else { if ($msg instanceof AuthenticateMessage) { if ($this->getAuthenticationManager() !== null) { $this->getAuthenticationManager()->onAuthenticationMessage($this, $session, $msg); } else { // TODO: should shut down here probably $this->manager->error("Authenticate sent to realm without auth manager."); } } else { $this->manager->error("Unhandled message sent to unauthenticated realm: " . $msg->getMsgCode()); $session->sendMessage(new AbortMessage(new \stdClass(), "wamp.error.not_authorized")); $session->shutdown(); } } } else { $handled = false; /* @var $role AbstractRole */ foreach ($this->roles as $role) { if ($role->handlesMessage($msg)) { $role->onMessage($session, $msg); $handled = true; break; } } if (!$handled) { $this->manager->warning("Unhandled message sent to \"{$this->getRealmName()}\": {$msg->getSerializedMessage()}"); } } }
/** * @param AbstractSession $session * @param Message $msg * @return mixed */ public function onMessage(AbstractSession $session, Message $msg) { if ($msg instanceof SubscribedMessage) { $this->processSubscribed($session, $msg); } elseif ($msg instanceof UnsubscribedMessage) { $this->processUnsubscribed($session, $msg); } elseif ($msg instanceof EventMessage) { $this->processEvent($session, $msg); } else { $session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg)); } }
/** * Process a result as a promise * * @param \React\Promise\Promise $promise * @param \Thruway\Message\InvocationMessage $msg * @param \Thruway\ClientSession $session * @param array $registration */ private function processResultAsPromise(Promise $promise, InvocationMessage $msg, ClientSession $session, $registration) { $promise->then(function ($promiseResults) use($msg, $session) { $options = new \stdClass(); if ($promiseResults instanceof Result) { $yieldMsg = new YieldMessage($msg->getRequestId(), $options, $promiseResults->getArguments(), $promiseResults->getArgumentsKw()); } else { $promiseResults = is_array($promiseResults) ? $promiseResults : [$promiseResults]; $promiseResults = !$this::is_list($promiseResults) ? [$promiseResults] : $promiseResults; $yieldMsg = new YieldMessage($msg->getRequestId(), $options, $promiseResults); } $session->sendMessage($yieldMsg); }, function () use($msg, $session, $registration) { $errorMsg = ErrorMessage::createErrorMessageFromMessage($msg); $errorMsg->setErrorURI($registration['procedure_name'] . '.error'); $session->sendMessage($errorMsg); }, function ($results) use($msg, $session, $registration) { $options = ["progress" => true]; if ($results instanceof Result) { $yieldMsg = new YieldMessage($msg->getRequestId(), $options, $results->getArguments(), $results->getArgumentsKw()); } else { $results = is_array($results) ? $results : [$results]; $results = !$this::is_list($results) ? [$results] : $results; $yieldMsg = new YieldMessage($msg->getRequestId(), $options, $results); } $session->sendMessage($yieldMsg); }); }
/** * Process call * * @param \Thruway\Session $session * @param Call $call * @throws \Exception * @return bool | Call */ public function processCall(Session $session, Call $call) { // find a registration to call if (count($this->registrations) == 0) { $session->sendMessage(ErrorMessage::createErrorMessageFromMessage($call->getCallMessage(), 'wamp.error.no_such_procedure')); return false; } // just send it to the first one if we don't allow multiple registrations if (!$this->getAllowMultipleRegistrations()) { $this->registrations[0]->processCall($call); } else { $this->callQueue->enqueue($call); $this->processQueue(); } return true; }
private function processInvocationError(Session $session, ErrorMessage $msg) { $call = $this->getCallByRequestId($msg->getRequestId()); if (!$call) { $errorMsg = ErrorMessage::createErrorMessageFromMessage($msg); $this->manager->error('No call for invocation error message: ' . $msg->getRequestId()); // TODO: do we send a message back to the callee? $errorMsg->setErrorURI('wamp.error.no_such_procedure'); $session->sendMessage($errorMsg); return false; } $this->calls->detach($call); $errorMsg = ErrorMessage::createErrorMessageFromMessage($call->getCallMessage()); $errorMsg->setErrorURI($msg->getErrorURI()); $errorMsg->setArguments($msg->getArguments()); $errorMsg->setArgumentsKw($msg->getArgumentsKw()); $call->getCallerSession()->sendMessage($errorMsg); }
/** * Process All Messages if the session has been authenticated * * @param \Thruway\Session $session * @param \Thruway\Message\Message $msg */ private function processAuthenticated(Session $session, Message $msg) { // authorization stuff here if ($msg instanceof ActionMessageInterface) { if (!$this->getAuthorizationManager()->isAuthorizedTo($session, $msg)) { Logger::alert($this, "Permission denied: " . $msg->getActionName() . " " . $msg->getUri() . " for " . $session->getAuthenticationDetails()->getAuthId()); $session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, "wamp.error.not_authorized")); return; } } $handled = false; foreach ($this->roles as $role) { if ($role->handlesMessage($msg)) { $role->onMessage($session, $msg); $handled = true; break; } } if (!$handled) { Logger::warning($this, "Unhandled message sent to \"{$this->getRealmName()}\""); } }
/** * @param Session $session * @param ErrorMessage $msg */ private function processInterruptError(Session $session, ErrorMessage $msg) { $call = isset($this->callInterruptIndex[$msg->getRequestId()]) ? $this->callInterruptIndex[$msg->getRequestId()] : null; if (!$call) { Logger::warning($this, "Interrupt error with no corresponding interrupt index"); return; } $errorMsgToCaller = ErrorMessage::createErrorMessageFromMessage($call->getCancelMessage()); $errorMsgToCaller->setErrorURI($msg->getErrorURI()); $callerSession = $call->getCallerSession(); $callerSession->sendMessage($errorMsgToCaller); $call->getRegistration()->removeCall($call); $this->removeCall($call); }
/** * processCancel processes cancel message from the caller. * Return true if the Call should be removed from active calls * * @param Session $session * @param CancelMessage $msg * @return bool */ public function processCancel(Session $session, CancelMessage $msg) { if ($this->getCallerSession() !== $session) { Logger::warning($this, "session attempted to cancel call they did not own."); return false; } if ($this->getCalleeSession() === null) { // this call has not been sent to a callee yet (it is in a queue) // we can just kill it and say it was canceled $errorMsg = ErrorMessage::createErrorMessageFromMessage($msg, "wamp.error.canceled"); $details = $errorMsg->getDetails() ?: (object) []; $details->_thruway_removed_from_queue = true; $session->sendMessage($errorMsg); return true; } $details = (object) []; if ($this->getCalleeSession()->getHelloMessage() instanceof HelloMessage) { $details = $this->getCalleeSession()->getHelloMessage()->getDetails(); } $calleeSupportsCancel = false; if (isset($details->roles->callee->features->call_canceling) && is_scalar($details->roles->callee->features->call_canceling)) { $calleeSupportsCancel = (bool) $details->roles->callee->features->call_canceling; } if (!$calleeSupportsCancel) { $errorMsg = ErrorMessage::createErrorMessageFromMessage($msg); $errorMsg->setErrorURI('wamp.error.not_supported'); $session->sendMessage($errorMsg); return false; } $this->setCancelMessage($msg); $this->canceling = true; $calleeSession = $this->getCalleeSession(); $interruptMessage = new InterruptMessage($this->getInvocationRequestId(), (object) []); $calleeSession->sendMessage($interruptMessage); $this->setInterruptMessage($interruptMessage); if (isset($msg->getOptions()->mode) && is_scalar($msg->getOptions()->mode) && $msg->getOptions()->mode == "killnowait") { $errorMsg = ErrorMessage::createErrorMessageFromMessage($msg, "wamp.error.canceled"); $session->sendMessage($errorMsg); return true; } return false; }
public function testInvocationError() { $dealer = new \Thruway\Role\Dealer(); $callerTransport = new \Thruway\Transport\DummyTransport(); $callerSession = new Session($callerTransport); $calleeTransport = new \Thruway\Transport\DummyTransport(); $calleeSession = new Session($calleeTransport); // register from callee $registerMsg = new \Thruway\Message\RegisterMessage(1, new stdClass(), 'test_proc_name'); $dealer->handleRegisterMessage(new \Thruway\Event\MessageEvent($calleeSession, $registerMsg)); $this->assertInstanceOf('\\Thruway\\Message\\RegisteredMessage', $calleeTransport->getLastMessageSent()); // call from one session $callRequestId = \Thruway\Common\Utils::getUniqueId(); $callMsg = new \Thruway\Message\CallMessage($callRequestId, new stdClass(), 'test_proc_name'); $dealer->handleCallMessage(new \Thruway\Event\MessageEvent($callerSession, $callMsg)); $this->assertInstanceOf('\\Thruway\\Message\\InvocationMessage', $calleeTransport->getLastMessageSent()); $errorMsg = \Thruway\Message\ErrorMessage::createErrorMessageFromMessage($calleeTransport->getLastMessageSent(), 'the.error.uri'); $dealer->handleErrorMessage(new \Thruway\Event\MessageEvent($calleeSession, $errorMsg)); /** @var \Thruway\Message\ErrorMessage $returnedError */ $returnedError = $callerTransport->getLastMessageSent(); $this->assertInstanceOf('\\Thruway\\Message\\ErrorMessage', $returnedError); $this->assertEquals(Message::MSG_CALL, $returnedError->getErrorMsgCode()); $this->assertEquals($callRequestId, $returnedError->getErrorRequestId()); $this->assertEquals('the.error.uri', $returnedError->getErrorURI()); }
/** * @param Session $session * @param UnsubscribeMessage $msg * @return UnsubscribedMessage */ public function processUnsubscribe(Session $session, UnsubscribeMessage $msg) { $subscription = $this->getSubscriptionById($msg->getSubscriptionId()); if (!$subscription || !isset($this->topics[$subscription->getTopic()])) { $errorMsg = ErrorMessage::createErrorMessageFromMessage($msg); $session->sendMessage($errorMsg->setErrorURI('wamp.error.no_such_subscription')); } $topicName = $subscription->getTopic(); $subscribers = $this->topics[$topicName]; /* @var $subscriber Session */ foreach ($this->topics[$topicName] as $key => $subscriber) { if ($subscriber == $session) { unset($subscribers[$key]); } } $this->subscriptions->detach($subscription); $session->sendMessage(new UnsubscribedMessage($msg->getRequestId())); }
/** * Process Unsubscribe message * * @param \Thruway\Session $session * @param \Thruway\Message\UnsubscribeMessage $msg */ protected function processUnsubscribe(Session $session, UnsubscribeMessage $msg) { $subscription = false; // should probably be more efficient about this - maybe later /** @var SubscriptionGroup $subscriptionGroup */ foreach ($this->subscriptionGroups as $subscriptionGroup) { $result = $subscriptionGroup->processUnsubscribe($session, $msg); if ($result !== false) { $subscription = $result; } } if ($subscription === false) { $errorMsg = ErrorMessage::createErrorMessageFromMessage($msg); $session->sendMessage($errorMsg->setErrorURI('wamp.error.no_such_subscription')); return; } }
/** * This will send error messages on all pending calls * This is used when a session disconnects before completing a call */ public function errorAllPendingCalls() { foreach ($this->calls as $call) { $call->getCallerSession()->sendMessage(ErrorMessage::createErrorMessageFromMessage($call->getCallMessage(), 'wamp.error.canceled')); } }
/** * @param ClientSession $session * @param InvocationMessage $msg */ public function processInvocation(ClientSession $session, InvocationMessage $msg) { foreach ($this->registrations as $key => $registration) { if (!isset($registration["registration_id"])) { $this->logger->info("Registration_id not set for " . $registration['procedure_name'] . "\n"); } else { if ($registration["registration_id"] === $msg->getRegistrationId()) { if ($registration['callback'] === null) { // this is where calls end up if the client has called unregister but // have not yet received confirmation from the router about the // unregistration $session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, "thruway.error.unregistering")); return; } $results = $registration["callback"]($msg->getArguments(), $msg->getArgumentsKw(), $msg->getDetails()); if ($results instanceof Promise) { // the result is a promise - hook up stuff as a callback $results->then(function ($promiseResults) use($msg, $session) { $promiseResults = is_array($promiseResults) ? $promiseResults : [$promiseResults]; $promiseResults = !$this::is_list($promiseResults) ? [$promiseResults] : $promiseResults; $options = new \stdClass(); $yieldMsg = new YieldMessage($msg->getRequestId(), $options, $promiseResults); $session->sendMessage($yieldMsg); }, function ($errorUri = null, $errorArgs = null, $errorArgsKw = null) use($msg, $session, $registration) { $errorMsg = ErrorMessage::createErrorMessageFromMessage($msg); if ($errorUri !== null) { $errorMsg->setErrorURI($registration['procedure_name'] . '.error'); } else { $errorMsg->setErrorURI("thruway.invocation.error"); } if (is_array($errorArgs)) { $errorMsg->setArguments($errorArgs); } if (is_array($errorArgsKw)) { $errorMsg->setArgumentsKw($errorArgsKw); } $session->sendMessage($errorMsg); }); } else { $results = !$this::is_list($results) ? [$results] : $results; $options = new \stdClass(); $yieldMsg = new YieldMessage($msg->getRequestId(), $options, $results); $session->sendMessage($yieldMsg); } break; } } } }
public function handleHelloMessage(Realm $realm, Session $session, HelloMessage $msg) { $requestedMethods = $msg->getAuthMethods(); $sentMessage = false; // go through our authMethods and see which one matches first foreach ($this->authMethods as $authMethod => $authMethodInfo) { if (in_array($authMethod, $requestedMethods) && (in_array($realm->getRealmName(), $authMethodInfo['auth_realms']) || in_array("*", $authMethodInfo['auth_realms']))) { // we can agree on something $authDetails = new AuthenticationDetails(); $authDetails->setAuthMethod($authMethod); $helloDetails = $msg->getDetails(); if (isset($helloDetails['authid'])) { $authDetails->setAuthId($helloDetails['authid']); } $session->setAuthenticationDetails($authDetails); $sessionInfo = array("sessionId" => $session->getSessionId(), "realm" => $realm->getRealmName()); $this->session->call($authMethodInfo['handlers']['onhello'], array($msg, $sessionInfo))->then(function ($res) use($session, $msg) { // this is handling the return of the onhello RPC call if (!is_array($res)) { $session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg)); return; } if (count($res) < 2) { $session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg)); return; } if ($res[0] == "CHALLENGE") { // TODO: validate challenge message $authMethod = $res[1]['challenge_method']; $challenge = $res[1]['challenge']; $session->getAuthenticationDetails()->setChallenge($challenge); $session->getAuthenticationDetails()->setChallengeDetails($res[1]); $session->sendMessage(new ChallengeMessage($authMethod, $session->getAuthenticationDetails()->getChallengeDetails())); } else { if ($res[0] == "NOCHALLENGE") { $session->sendMessage(new WelcomeMessage($session->getSessionId(), array("authid" => $res[1]["authid"], "authmethod" => $session->getAuthenticationDetails()->getAuthMethod()))); } else { if ($res[0] == "ERROR") { $session->sendMessage(new AbortMessage(new \stdClass(), "authentication_failure")); } else { $session->sendMessage(new AbortMessage(new \stdClass(), "authentication_failure")); } } } }); $sentMessage = true; } } /* * If we've gotten this far without sending a message, it means that no auth methods were sent by the client or the auth method sent * by the client hasn't been registered for this realm, so we need to check if there are any auth providers registered for the realm. * If there are auth provides registered then Abort. Otherwise we can send a welcome message. */ if (!$sentMessage) { if ($this->realmHasAuthProvider($realm->getRealmName())) { $session->sendMessage(new AbortMessage(new \stdClass(), "realm_authorization_failure")); } else { //Logged in as anonymous $session->setAuthenticationDetails(AuthenticationDetails::createAnonymous()); $roles = array("broker" => new \stdClass(), "dealer" => new \stdClass()); $session->sendMessage(new WelcomeMessage($session->getSessionId(), array("roles" => $roles))); $session->setAuthenticated(true); } } }