Transfer-Encoding: code
Nov. 6th, 2011 03:58 pmЯк і обіцяв, код:
Наведений раніше семпл містить помилку. Справа в тому що у рядку із chunk length можна крім довжини chunk вказати різні параметри після крапки з комою - так звані "chunk extensions" у вигляді key=value, розділені крапкою з комою. Якщо chunk extensions не використовуються - крапка з комою не ставляться. За стандартом ці extensions можна сміливо ігнорувати. Парсер довжини chunk наведений у семплі використовує квантор "+" для "зайвих" символів, а треба використовувати квантор "*". Ну і там ще у тому ж парсері проблеми з визначенням довжини рядка з chunk length.
Тепер щодо buffer overflow. Якщо при використанні read_until розміру буферу не вистачить для пошуку то функція завершиться з помилкою "Element not found". Наведений вище код коректно веде себе в умовах коли chunk body не влазить у бувер - оброблює його "по шматкам". Якщо ж у буфер не влазить рядок з chunk length то він завершується з помилкою "Element not found". Перевіряв це змінюючи max_size для streambuf.
void Client::_handleReadChunkLength(const boost::system::error_code & error) { if (!error) { size_t length = 0; _response.consume(parseChunkLength(_response.data(), length)); if (length == 0) { if (!_eofCallback.empty()) _eofCallback(); _shutdown(); } else { if (_response.size() < length + 2) { async_read( _socket, _response, transfer_at_least(length + 2 - _response.size()), boost::bind( &Client::_handleReadChunkData, this, placeholders::error, length ) ); } else { _handleReadChunkData(boost::system::error_code(), length); } } } else if (error == error::eof) { if (!_eofCallback.empty()) _eofCallback(); _shutdown(); } else if (error != error::operation_aborted) { if (!_errorCallback.empty()) _errorCallback(error); _shutdown(); } } void Client::_handleReadChunkData(const boost::system::error_code & error, size_t size) { if (size > 0) { if (size > _response.size()) { if (!_dataCallback.empty()) _dataCallback(_response.data()); } else { if (!_dataCallback.empty()) _dataCallback(buffer(_response.data(), size)); } } if (!error) { if (size > _response.size()) { const size_t remainder = size + 2 - _response.size(); _response.consume(_response.size()); async_read( _socket, _response, transfer_at_least(remainder), boost::bind( &Client::_handleReadChunkData, this, placeholders::error, remainder - 2 ) ); } else { _response.consume(size + 2); async_read_until( _socket, _response, "\r\n", boost::bind( &Client::_handleReadChunkLength, this, placeholders::error ) ); } } else if (error == error::eof) { if (!_eofCallback.empty()) _eofCallback(); _shutdown(); } else if (error != error::operation_aborted) { if (!_errorCallback.empty()) _errorCallback(error); _shutdown(); } } |
| _Winnie C++ Colorizer |
Наведений раніше семпл містить помилку. Справа в тому що у рядку із chunk length можна крім довжини chunk вказати різні параметри після крапки з комою - так звані "chunk extensions" у вигляді key=value, розділені крапкою з комою. Якщо chunk extensions не використовуються - крапка з комою не ставляться. За стандартом ці extensions можна сміливо ігнорувати. Парсер довжини chunk наведений у семплі використовує квантор "+" для "зайвих" символів, а треба використовувати квантор "*". Ну і там ще у тому ж парсері проблеми з визначенням довжини рядка з chunk length.
Тепер щодо buffer overflow. Якщо при використанні read_until розміру буферу не вистачить для пошуку то функція завершиться з помилкою "Element not found". Наведений вище код коректно веде себе в умовах коли chunk body не влазить у бувер - оброблює його "по шматкам". Якщо ж у буфер не влазить рядок з chunk length то він завершується з помилкою "Element not found". Перевіряв це змінюючи max_size для streambuf.