| 140 | } |
| 141 | |
| 142 | bool THttpParser::ChunkedContentParser() { |
| 143 | if (BodyNotExpected_) { |
| 144 | // RFC9112 6.1,6.3 |
| 145 | return OnEndParsing(); |
| 146 | } |
| 147 | |
| 148 | DBGOUT("ReadChunkedContent"); |
| 149 | TChunkInputState& ci = *ChunkInputState_; |
| 150 | |
| 151 | if (Content_.capacity() < static_cast<size_t>(DataEnd_ - Data_)) { |
| 152 | //try reduce memory reallocations |
| 153 | Content_.reserve(DataEnd_ - Data_); |
| 154 | } |
| 155 | |
| 156 | do { |
| 157 | if (!ci.LeftBytes_) { |
| 158 | if (Y_UNLIKELY(!ReadLine())) { //read first chunk size or CRLF from prev chunk or CRLF from last chunk |
| 159 | break; |
| 160 | } |
| 161 | |
| 162 | if (Y_UNLIKELY(ci.ReadLastChunk_)) { |
| 163 | return OnEndParsing(); |
| 164 | } |
| 165 | |
| 166 | if (!CurrentLine_) { |
| 167 | // skip crlf from previous chunk |
| 168 | if (!ReadLine()) { |
| 169 | break; |
| 170 | } |
| 171 | } |
| 172 | Y_ENSURE(CurrentLine_.size(), "NEH: LeftBytes hex number cannot be empty. "); |
| 173 | size_t size = CurrentLine_.find_first_of(" \t;"); |
| 174 | if (size == TString::npos) { |
| 175 | size = CurrentLine_.size(); |
| 176 | } |
| 177 | ci.LeftBytes_ = IntFromString<ui32, 16, char>(CurrentLine_.c_str(), size); |
| 178 | CurrentLine_.remove(0); |
| 179 | if (!ci.LeftBytes_) { //detectect end of context marker - zero-size chunk, need read CRLF after empty chunk |
| 180 | ci.ReadLastChunk_ = true; |
| 181 | if (ReadLine()) { |
| 182 | return OnEndParsing(); |
| 183 | } else { |
| 184 | break; |
| 185 | } |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | size_t rd = Min<size_t>(DataEnd_ - Data_, ci.LeftBytes_); |
| 190 | Content_.append(Data_, rd); |
| 191 | Data_ += rd; |
| 192 | ci.LeftBytes_ -= rd; |
| 193 | } while (Data_ != DataEnd_); |
| 194 | |
| 195 | Parser_ = &THttpParser::ChunkedContentParser; |
| 196 | return false; |
| 197 | } |
| 198 | |
| 199 | bool THttpParser::OnEndParsing() { |