SNode.C
Loading...
Searching...
No Matches
Chunked.cpp
Go to the documentation of this file.
1/*
2 * snode.c - a slim toolkit for network communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "web/http/decoder/Chunked.h"
21
22#include "core/socket/stream/SocketContext.h"
23
24#include <stdexcept>
25
26#ifndef DOXYGEN_SHOULD_SKIP_THIS
27
28#endif // DOXYGEN_SHOULD_SKIP_THIS
29
30namespace web::http::decoder {
31
32 Chunked::Chunked(const core::socket::stream::SocketContext* socketContext)
33 : socketContext(socketContext) {
34 }
35
36 std::size_t Chunked::read() {
37 std::size_t consumed = 0;
38 std::size_t ret = 0;
39
40 switch (state) {
41 case -1:
42 content.clear();
43 completed = false;
44 error = false;
45
46 state = 0;
47
48 [[fallthrough]];
49 case 0:
50 do {
51 ret = chunk.read(socketContext);
52 consumed += ret;
53
54 if (chunk.isComplete()) {
55 content.insert(content.end(), chunk.begin(), chunk.end());
56
57 completed = chunk.size() == 0;
58
59 } else if (chunk.isError()) {
60 error = true;
61 }
62
63 state = (completed || error) ? -1 : state;
64 } while (ret > 0 && !completed);
65 break;
66 }
67
68 return consumed;
69 }
70
72 }
73
74 inline std::size_t Chunked::Chunk::read(const core::socket::stream::SocketContext* socketContext) {
75 std::size_t consumed = 0;
76 std::size_t ret = 0;
77 std::size_t pos = 0;
78
79 const static int maxChunkLenTotalS = sizeof(std::size_t) * 2;
80
81 switch (state) {
82 case -1: // Re-init
83 CR = false;
84 LF = false;
85 chunkLenTotalS.clear();
86 chunkLenTotal = 0;
87 chunkLenRead = 0;
88
89 completed = false;
90 error = false;
91
92 state = 0;
93
94 [[fallthrough]];
95 case 0: // ChunklLenS
96 do {
97 char ch = '\0';
98
99 ret = socketContext->readFromPeer(&ch, 1);
100 if (ret > 0) {
101 consumed++;
102
103 if (CR) {
104 if (ch == '\n') {
105 LF = true;
106 } else {
107 error = true;
108 }
109 } else if (ch == '\r') {
110 CR = true;
111 } else {
112 chunkLenTotalS += ch;
113 }
114 }
115 } while (!error && ret > 0 && !(CR && LF) && chunkLenTotalS.size() <= maxChunkLenTotalS);
116
117 if (!(CR && LF)) {
118 error = !error ? chunkLenTotalS.size() > maxChunkLenTotalS : error;
119
120 if (error) {
121 state = -1;
122 }
123
124 break;
125 }
126
127 CR = false;
128 LF = false;
129
130 try {
131 chunkLenTotal = std::stoul(chunkLenTotalS, &pos, 16);
132 chunk.resize(chunkLenTotal);
133
134 state = 1;
135 } catch (std::invalid_argument&) {
136 error = true;
137 break;
138 }
139
140 [[fallthrough]];
141 case 1: // ChunkData
142 do {
143 ret = socketContext->readFromPeer(chunk.data() + chunkLenRead, chunkLenTotal - chunkLenRead);
144 chunkLenRead += ret;
145 consumed += ret;
146 } while (ret > 0 && (chunkLenTotal != chunkLenRead));
147
149 break;
150 }
151
152 state = 2;
153
154 [[fallthrough]];
155 case 2: // Closing "\r\n"
156 char ch = '\0';
157
158 ret = socketContext->readFromPeer(&ch, 1);
159 consumed += ret;
160
161 if (ret > 0) {
162 if (CR || ch == '\r') {
163 if (CR && ch == '\n') {
164 LF = true;
165 completed = true;
166 state = -1;
167 } else if (CR) {
168 error = true;
169 state = -1;
170 } else {
171 CR = true;
172 }
173 } else {
174 error = true;
175 state = -1;
176 }
177 }
178
179 break;
180 }
181
182 return consumed;
183 }
184
185 inline bool Chunked::Chunk::isError() const {
186 return error;
187 }
188
189 inline bool Chunked::Chunk::isComplete() const {
190 return completed;
191 }
192
194 return chunk.begin();
195 }
196
198 return chunk.end();
199 }
200
201 std::size_t Chunked::Chunk::size() {
202 return chunk.size();
203 }
204
205} // namespace web::http::decoder
std::size_t readFromPeer(char *chunk, std::size_t chunklen) const final
std::size_t read(const core::socket::stream::SocketContext *socketContext)
Definition Chunked.cpp:74
std::vector< char >::iterator begin()
Definition Chunked.cpp:193
std::vector< char >::iterator end()
Definition Chunked.cpp:197
Chunked(const core::socket::stream::SocketContext *socketContext)
Definition Chunked.cpp:32
const core::socket::stream::SocketContext * socketContext
Definition Chunked.h:91
std::size_t read() override
Definition Chunked.cpp:36