-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsse.cppm
More file actions
163 lines (139 loc) · 4.71 KB
/
sse.cppm
File metadata and controls
163 lines (139 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
export module mcpplibs.tinyhttps:sse;
import std;
namespace mcpplibs::tinyhttps {
export struct SseEvent {
std::string event; // event type (default "message")
std::string data; // event data
std::string id; // event id (optional)
};
export class SseParser {
private:
std::string buffer_;
std::string currentEvent_;
std::string currentData_;
std::string currentId_;
public:
std::vector<SseEvent> feed(std::string_view chunk) {
buffer_.append(chunk);
std::vector<SseEvent> events;
// Scan for event boundaries: \n\n or \r\n\r\n
while (true) {
// Find double newline (event boundary)
auto pos = find_event_boundary_();
if (pos == std::string::npos) {
break;
}
// Extract the event block
std::string_view block(buffer_.data(), pos);
// Determine how many chars to skip past the boundary
std::size_t skip = 0;
if (pos + 1 < buffer_.size() && buffer_[pos] == '\n' && buffer_[pos + 1] == '\n') {
skip = pos + 2;
} else if (pos + 3 < buffer_.size() &&
buffer_[pos] == '\r' && buffer_[pos + 1] == '\n' &&
buffer_[pos + 2] == '\r' && buffer_[pos + 3] == '\n') {
skip = pos + 4;
} else {
skip = pos + 2; // fallback for \n\n
}
// Process each line in the block
process_block_(block);
dispatch_event_(events);
buffer_.erase(0, skip);
}
return events;
}
void reset() {
buffer_.clear();
currentEvent_.clear();
currentData_.clear();
currentId_.clear();
}
private:
std::size_t find_event_boundary_() const {
for (std::size_t i = 0; i < buffer_.size(); ++i) {
if (buffer_[i] == '\n' && i + 1 < buffer_.size() && buffer_[i + 1] == '\n') {
return i;
}
if (buffer_[i] == '\r' && i + 3 < buffer_.size() &&
buffer_[i + 1] == '\n' && buffer_[i + 2] == '\r' && buffer_[i + 3] == '\n') {
return i;
}
}
return std::string::npos;
}
void process_block_(std::string_view block) {
while (!block.empty()) {
// Find end of line
std::size_t lineEnd = 0;
std::size_t skip = 0;
bool found = false;
for (std::size_t i = 0; i < block.size(); ++i) {
if (block[i] == '\r' && i + 1 < block.size() && block[i + 1] == '\n') {
lineEnd = i;
skip = i + 2;
found = true;
break;
}
if (block[i] == '\n') {
lineEnd = i;
skip = i + 1;
found = true;
break;
}
}
if (!found) {
lineEnd = block.size();
skip = block.size();
}
process_line_(block.substr(0, lineEnd));
block = block.substr(skip);
}
}
void process_line_(std::string_view line) {
if (line.empty()) {
return;
}
// Comment line
if (line[0] == ':') {
return;
}
// Find colon
auto colonPos = line.find(':');
if (colonPos == std::string_view::npos) {
// Field with no value — treat field name as the whole line, value as empty
return;
}
auto field = line.substr(0, colonPos);
auto value = line.substr(colonPos + 1);
// Strip single leading space from value if present
if (!value.empty() && value[0] == ' ') {
value = value.substr(1);
}
if (field == "data") {
if (!currentData_.empty()) {
currentData_ += '\n';
}
currentData_.append(value);
} else if (field == "event") {
currentEvent_ = std::string(value);
} else if (field == "id") {
currentId_ = std::string(value);
}
// Other fields ignored
}
void dispatch_event_(std::vector<SseEvent>& events) {
if (currentData_.empty() && currentEvent_.empty() && currentId_.empty()) {
return;
}
SseEvent ev;
ev.event = currentEvent_.empty() ? "message" : std::move(currentEvent_);
ev.data = std::move(currentData_);
ev.id = std::move(currentId_);
events.push_back(std::move(ev));
currentEvent_.clear();
currentData_.clear();
currentId_.clear();
}
};
} // namespace mcpplibs::tinyhttps