SNode.C
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
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/*
21 * MIT License
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 */
41
42#include "web/http/decoder/Chunked.h"
43
44#include "core/socket/stream/SocketContext.h"
45
46#include <stdexcept>
47
48#ifndef DOXYGEN_SHOULD_SKIP_THIS
49
50#endif // DOXYGEN_SHOULD_SKIP_THIS
51
52namespace web::http::decoder {
53
54 Chunked::Chunked(const core::socket::stream::SocketContext* socketContext)
55 : socketContext(socketContext) {
56 }
57
58 std::size_t Chunked::read() {
59 std::size_t consumed = 0;
60 std::size_t ret = 0;
61
62 switch (state) {
63 case -1:
64 content.clear();
65 completed = false;
66 error = false;
67
68 state = 0;
69
70 [[fallthrough]];
71 case 0:
72 do {
74 consumed += ret;
75
78
80
81 } else if (chunk.isError()) {
82 error = true;
83 }
84
85 state = (completed || error) ? -1 : state;
86 } while (ret > 0 && !completed);
87 break;
88 }
89
90 return consumed;
91 }
92
94 }
95
96 inline std::size_t Chunked::Chunk::read(const core::socket::stream::SocketContext* socketContext) {
97 std::size_t consumed = 0;
98 std::size_t ret = 0;
99 std::size_t pos = 0;
100
101 const static int maxChunkLenTotalS = sizeof(std::size_t) * 2;
102
103 switch (state) {
104 case -1: // Re-init
105 CR = false;
106 LF = false;
107 chunkLenTotalS.clear();
108 chunkLenTotal = 0;
109 chunkLenRead = 0;
110
111 completed = false;
112 error = false;
113
114 state = 0;
115
116 [[fallthrough]];
117 case 0: // ChunklLenS
118 do {
119 char ch = '\0';
120
121 ret = socketContext->readFromPeer(&ch, 1);
122 if (ret > 0) {
123 consumed++;
124
125 if (CR) {
126 if (ch == '\n') {
127 LF = true;
128 } else {
129 error = true;
130 }
131 } else if (ch == '\r') {
132 CR = true;
133 } else {
134 chunkLenTotalS += ch;
135 }
136 }
137 } while (!error && ret > 0 && !(CR && LF) && chunkLenTotalS.size() <= maxChunkLenTotalS);
138
139 if (!(CR && LF)) {
140 error = !error ? chunkLenTotalS.size() > maxChunkLenTotalS : error;
141
142 if (error) {
143 state = -1;
144 }
145
146 break;
147 }
148
149 CR = false;
150 LF = false;
151
152 try {
153 chunkLenTotal = std::stoul(chunkLenTotalS, &pos, 16);
154 chunk.resize(chunkLenTotal);
155
156 state = 1;
157 } catch (std::invalid_argument&) {
158 error = true;
159 break;
160 }
161
162 [[fallthrough]];
163 case 1: // ChunkData
164 do {
166 chunkLenRead += ret;
167 consumed += ret;
168 } while (ret > 0 && (chunkLenTotal != chunkLenRead));
169
171 break;
172 }
173
174 state = 2;
175
176 [[fallthrough]];
177 case 2: // Closing "\r\n"
178 char ch = '\0';
179
180 ret = socketContext->readFromPeer(&ch, 1);
181 consumed += ret;
182
183 if (ret > 0) {
184 if (CR || ch == '\r') {
185 if (CR && ch == '\n') {
186 LF = true;
187 completed = true;
188 state = -1;
189 } else if (CR) {
190 error = true;
191 state = -1;
192 } else {
193 CR = true;
194 }
195 } else {
196 error = true;
197 state = -1;
198 }
199 }
200
201 break;
202 }
203
204 return consumed;
205 }
206
207 inline bool Chunked::Chunk::isError() const {
208 return error;
209 }
210
211 inline bool Chunked::Chunk::isComplete() const {
212 return completed;
213 }
214
215 std::vector<char>::iterator Chunked::Chunk::begin() {
216 return chunk.begin();
217 }
218
219 std::vector<char>::iterator Chunked::Chunk::end() {
220 return chunk.end();
221 }
222
223 std::size_t Chunked::Chunk::size() {
224 return chunk.size();
225 }
226
227} // namespace web::http::decoder
std::size_t readFromPeer(char *chunk, std::size_t chunklen) const final
std::vector< char > content
std::size_t read(const core::socket::stream::SocketContext *socketContext)
Definition Chunked.cpp:96
std::vector< char >::iterator begin()
Definition Chunked.cpp:215
std::vector< char > chunk
Definition Chunked.h:86
std::vector< char >::iterator end()
Definition Chunked.cpp:219
Chunked(const core::socket::stream::SocketContext *socketContext)
Definition Chunked.cpp:54
const core::socket::stream::SocketContext * socketContext
Definition Chunked.h:113
std::size_t read() override
Definition Chunked.cpp:58