-
Notifications
You must be signed in to change notification settings - Fork 448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add basic backend for chat #429
Conversation
|
||
if ($this->userId === null) { | ||
$actorType = 'guests'; | ||
$actorId = $this->session->get('spreed-session'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this is too long?
Our session is 255, the actor id is only 64
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, yes, you are totally right :-S
How could a guest be identified then? Maybe storing a 64 char long hash of the session? Any other (probably better :-P ) idea?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either that, or use a hard-coded string instead. But I don't know what this implies? Can guests edit all users messages then?
Well they can't anyway in our chat, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with a hard-coded string is that then there would be no way to know which guest sent which message; that is why I was using the session in first place.
Regarding the edition of messages no, they can not be edited in our chat interface. Although now that you mention it maybe they can be edited through the Comments DAV or something like that... I will have to check it.
I think using the comments API is a good short cut for the first version. |
I have fixed the issue of the spreed-session being too long to be stored as an actorId by hashing the spreed-session. Please review again :-) |
383e4dc
to
3f38a13
Compare
lib/Controller/ChatController.php
Outdated
|
||
$creationDateTime = new \DateTime('now', new \DateTimeZone('UTC')); | ||
|
||
$this->chatManager->sendMessage($token, $actorType, $actorId, $message, $creationDateTime); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use $room->getId()
instead of $token
lib/Controller/ChatController.php
Outdated
*/ | ||
public function receiveMessages($token, $offset = 0, $notOlderThanTimestamp = 0, $timeout = 30) { | ||
try { | ||
$room = $this->manager->getRoomForParticipantByToken($token, $this->userId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to extend this, so it is only accessible when the user is listed, or has a valid session.
Otherwise you can read chat messages of password protected public rooms without the password.
So please use:
spreed/lib/Controller/CallController.php
Lines 83 to 99 in 86ec1dd
try { | |
$room = $this->manager->getRoomForSession($this->userId, $this->session->get('spreed-session')); | |
} catch (RoomNotFoundException $e) { | |
if ($this->userId === null) { | |
return new DataResponse([], Http::STATUS_NOT_FOUND); | |
} | |
// For logged in users we search for rooms where they are real participants | |
try { | |
$room = $this->manager->getRoomForParticipantByToken($token, $this->userId); | |
$room->getParticipant($this->userId); | |
} catch (RoomNotFoundException $e) { | |
return new DataResponse([], Http::STATUS_NOT_FOUND); | |
} catch (ParticipantNotFoundException $e) { | |
return new DataResponse([], Http::STATUS_NOT_FOUND); | |
} | |
} |
Same for sending Messages
If that makes your integration tests fail try And user "participant" joins call "room" with 200
The chat manager handles sending and receiving chat messages. It uses basic polling; "receiveMessages()" will repeteadly query the database for new messages, waiting a little between each query, until messages are found or the timeout expires. The Comments API is used internally, but as this class is meant to be used directly and only by a Controller, "receiveMessages()" returns an array of IComments too instead of a custom interface to save the conversion of objects. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
The ChatController uses a long-polling approach: if there are currently no messages the response will not be sent immediately; instead, HTTP connection will be kept open waiting for new messages to arrive and, when they do, then the response will be sent. The technique is simply based on the fact that ChatManager will wait (hang) until there are some messages to be returned, or until the timeout expires. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
The character limit for actorId is 64, but the spreed-session is 256 characters long, so it has to be hashed to get an ID that fits. The sha1 algorithm is used as, from all the hash functions bundled with PHP that are always available (the Hash extension can be disabled using the "--disable-hash" switch, unlike those that are part of the string functions), it generates the longest hashes (40 characters) that fit in the actorId column of the database. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
Before, any user was able to send and receive messages to and from public password protected rooms, even if they were not invited and they had not joined it; guests were not able to send, but were able to receive nevertheless. Now, only invited users or users/guests that have joined a public password protected room can send and receive messages to and from it. As a side effect that change affects too to regular public rooms (not password protected), but the new behaviour makes more sense than the old one anyway. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
3f38a13
to
ce91552
Compare
@nickvergessen I have addressed your comments in the last two commits. |
* @param string $token the token for the Room. | ||
* @return \OCA\Spreed\Room|null the Room, or null if none was found. | ||
*/ | ||
private function getRoom($token) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
little remark for the future, please use protected rather then private, makes testing and other things a lot easier
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used private because this class is not meant to be extended and because, in general, I prefer to not change a design just to make testing easier (and if I am not mistaken this change would help only to be able to mock the method, as you could invoke it to explicitly test it by using reflection).
Having said that, I can change it to protected if you wish ;-)
Lets merge this for now, if there are bugs we can still fix them later |
This pull request adds a basic backend for the chat; the frontend will be added in later pull requests.
The backend provides two controller methods:
sendMessage
andreceiveMessages
.receiveMessages
uses a long-polling approach: it returns immediately only if there are messages available; otherwise it keeps the HTTP connection open and waits for new messages, returning them as soon as they are available (in which case a new HTTP request must be done by the client). The HTTP connection is not kept open indefinitely, though; the default timeout is 30 seconds, and the maximum timeout that can be set is 60 seconds (I have used those values because they felt right; they are not related to typical values for web server timeouts or anything like that).Note that, at this time, chat messages are never removed; a later pull request will ensure that when a room is deleted all its chat messages are also deleted.
Internally for chat messages the Comments API is used; only a subset of its features is needed (for example, there is no need for parent-child relationships in chat messages), but it fits well with the chat messages anyway and is already mature code, so using it seemed like the best approach. The Comments API is not used directly by the
ChatController
, though; theChatManager
class sits between them and is the one that takes care of calling the Comments API as needed and also of waiting for new messages to be available when trying to receive them.The
ChatManager
uses polling to the Comments API (and this, in turn, to the database): it queries the Comments API to see if there are available messages; if there are it returns them, and if not, it sleeps for a second and queries it again, repeating this process until there are messages or the timeout expires. The good thing about this approach is that it just works; there is no need to install specific extensions, or to configure anything. The bad thing is the stress caused to the server. Therefore, the idea is to create anotherChatManager
that will use interprocess communication to notify and wake up other PHP process when there are messages available, thus eliminating the need to poll the database.That more advanced
ChatManager
will not be a replacement but a complement for the currentChatManager
though; the more advanced one will be used if possible but, if not (for example if it requires PHP extensions that are not installed), it will fallback to the basic one. Given that otherChatManager
related classes/interfaces/files will be added in the future theChatManager
was placed on its ownChat
directory.Finally, although unit and integration tests are provided for the backend, for the time being there are no tests that truly verify that the long-polling works as expected; I will try to come with something later, as asynchronous tests would be really useful for the more advanced
ChatManager
version.