KUnifiedPush

serversenteventsstream.cpp
1/*
2 SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6#include "serversenteventsstream.h"
7#include "logging.h"
8
9#include <QIODevice>
10
11#include <algorithm>
12#include <cstring>
13
14using namespace KUnifiedPush;
15
16ServerSentEventsStream::ServerSentEventsStream(QObject *parent)
17 : QObject(parent)
18{
19}
20
21ServerSentEventsStream::~ServerSentEventsStream() = default;
22
23void ServerSentEventsStream::read(QIODevice *device)
24{
25 connect(device, &QIODevice::readyRead, this, [device, this]() {
26 m_buffer.append(device->read(device->bytesAvailable()));
27 processBuffer();
28 });
29}
30
31static bool isLineBreak(char c)
32{
33 return c == '\n' || c == '\r';
34}
35
36static QByteArray::ConstIterator findLineBreak(const QByteArray::const_iterator &begin, const QByteArray::const_iterator &end)
37{
38 return std::find_if(begin, end, isLineBreak);
39}
40
41static QByteArray::const_iterator consumeLineBreak(const QByteArray::const_iterator &begin, const QByteArray::const_iterator &end)
42{
43 auto it = begin;
44 if (it == end) {
45 } else if ((*it) == '\n') {
46 ++it;
47 } else if ((*it) == '\r') {
48 ++it;
49 if (it != end && (*it) == '\n') {
50 ++it;
51 }
52 }
53
54 return it;
55}
56
57static QByteArray::const_iterator findMessageEnd(const QByteArray::const_iterator &begin, const QByteArray::const_iterator &end)
58{
59 for (auto it = findLineBreak(begin, end); it != end; it = findLineBreak(it, end)) {
60 auto lookAhead = consumeLineBreak(it, end);
61 if (lookAhead != end && isLineBreak(*lookAhead)) {
62 return it;
63 }
64 it = lookAhead;
65 }
66
67 return end;
68}
69
70void ServerSentEventsStream::processBuffer()
71{
72 qCDebug(Log) << m_buffer;
73 auto msgEnd = findMessageEnd(m_buffer.begin(), m_buffer.end());
74 if (msgEnd == m_buffer.end()) {
75 qCDebug(Log) << "buffer incomplete, waiting for more";
76 return;
77 }
78
79 SSEMessage msg;
80 for (auto it = m_buffer.constBegin(); it != msgEnd;) {
81 auto lineBegin = it;
82 auto lineEnd = findLineBreak(lineBegin, msgEnd);
83 it = consumeLineBreak(lineEnd, msgEnd);
84 Q_ASSERT(lineBegin != it);
85
86 auto colonIt = std::find(lineBegin, lineEnd, ':');
87 auto valueBegin = colonIt;
88 if (valueBegin != lineEnd) {
89 ++valueBegin;
90 if (valueBegin != lineEnd && (*valueBegin) == ' ') {
91 ++valueBegin;
92 }
93 }
94
95 if (colonIt == lineBegin || valueBegin == lineEnd) {
96 continue; // comment or value-less field
97 }
98
99 const auto fieldNameLen = std::distance(lineBegin, colonIt);
100 if (fieldNameLen == 4 && std::strncmp(lineBegin, "data", fieldNameLen) == 0) {
101 msg.data.append(valueBegin, std::distance(valueBegin, lineEnd));
102 } else if (fieldNameLen == 5 && std::strncmp(lineBegin, "event", fieldNameLen) == 0) {
103 msg.event = QByteArray(valueBegin, std::distance(valueBegin, lineEnd));
104 }
105 }
106 Q_EMIT messageReceived(msg);
107
108 msgEnd = consumeLineBreak(msgEnd, m_buffer.end());
109 msgEnd = consumeLineBreak(msgEnd, m_buffer.end());
110 if (msgEnd == m_buffer.end()) {
111 m_buffer.clear();
112 } else {
113 const auto remainingLen = m_buffer.size() - std::distance(m_buffer.constBegin(), msgEnd);
114 std::memmove(m_buffer.begin(), msgEnd, remainingLen);
115 m_buffer.truncate(remainingLen);
116 processBuffer();
117 }
118}
const QList< QKeySequence > & begin()
const QList< QKeySequence > & end()
Client-side integration with UnifiedPush.
Definition connector.h:16
QByteArray & append(QByteArrayView data)
iterator begin()
void clear()
const_iterator constBegin() const const
typedef const_iterator
iterator end()
qsizetype size() const const
void truncate(qsizetype pos)
virtual qint64 bytesAvailable() const const
QByteArray read(qint64 maxSize)
void readyRead()
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:21:15 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.