summaryrefslogtreecommitdiffstats
path: root/net/http/http_chunked_decoder.cc
diff options
context:
space:
mode:
authorericroman@google.com <ericroman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-14 02:44:45 +0000
committerericroman@google.com <ericroman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-14 02:44:45 +0000
commitd89222accb371e210c434b27787fc80c30fa758d (patch)
tree19c518623c133f3a70b7549017db7e3c31b929bc /net/http/http_chunked_decoder.cc
parent293c0a7b4306a4cce26eb31b21587206c2d9a497 (diff)
downloadchromium_src-d89222accb371e210c434b27787fc80c30fa758d.zip
chromium_src-d89222accb371e210c434b27787fc80c30fa758d.tar.gz
chromium_src-d89222accb371e210c434b27787fc80c30fa758d.tar.bz2
Address some issues I found in chunked decoder:
(1) stricter parsing of chunk-size (don't allow leading/trailing whitespace, redundant "+" sign, leading "0x") (2) check for negative chunk sizes, and fail early rather than hitting weird stuff (3) don't mutate the const char* returned by std::string::data() (4) fail if CRLF terminator is missing on chunk-data. (why the spec has this in first place seems unecessary, since the chunk-size already tells the story...) (5) don't allow empty CRLFs git-svn-id: svn://svn.chromium.org/chrome/trunk/src@853 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http/http_chunked_decoder.cc')
-rw-r--r--net/http/http_chunked_decoder.cc78
1 files changed, 53 insertions, 25 deletions
diff --git a/net/http/http_chunked_decoder.cc b/net/http/http_chunked_decoder.cc
index 059d792..9c0bbcb 100644
--- a/net/http/http_chunked_decoder.cc
+++ b/net/http/http_chunked_decoder.cc
@@ -41,12 +41,15 @@
#include "net/http/http_chunked_decoder.h"
#include "base/logging.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
#include "net/base/net_errors.h"
namespace net {
HttpChunkedDecoder::HttpChunkedDecoder()
: chunk_remaining_(0),
+ chunk_terminator_remaining_(false),
reached_last_chunk_(false),
reached_eof_(false) {
}
@@ -63,66 +66,81 @@ int HttpChunkedDecoder::FilterBuf(char* buf, int buf_len) {
result += num;
buf += num;
+
+ // After each chunk's data there should be a CRLF
+ if (!chunk_remaining_)
+ chunk_terminator_remaining_ = true;
continue;
} else if (reached_eof_) {
break; // Done!
}
- // Returns bytes consumed or an error code.
- int rv = ScanForChunkRemaining(buf, buf_len);
- if (rv < 0)
- return rv;
+ int bytes_consumed = ScanForChunkRemaining(buf, buf_len);
+ if (bytes_consumed < 0)
+ return bytes_consumed; // Error
- buf_len -= rv;
+ buf_len -= bytes_consumed;
if (buf_len)
- memmove(buf, buf + rv, buf_len);
+ memmove(buf, buf + bytes_consumed, buf_len);
}
return result;
}
-int HttpChunkedDecoder::ScanForChunkRemaining(char* buf, int buf_len) {
+int HttpChunkedDecoder::ScanForChunkRemaining(const char* buf, int buf_len) {
DCHECK(chunk_remaining_ == 0);
DCHECK(buf_len > 0);
- int result = 0;
+ int bytes_consumed = 0;
- char *p = static_cast<char*>(memchr(buf, '\n', buf_len));
- if (p) {
- *p = 0;
- if ((p > buf) && (*(p - 1) == '\r')) // Eliminate a preceding CR.
- *(p - 1) = 0;
- result = static_cast<int>(p - buf) + 1;
+ size_t index_of_lf = StringPiece(buf, buf_len).find('\n');
+ if (index_of_lf != StringPiece::npos) {
+ buf_len = static_cast<int>(index_of_lf);
+ if (buf_len && buf[buf_len - 1] == '\r') // Eliminate a preceding CR.
+ buf_len--;
+ bytes_consumed = static_cast<int>(index_of_lf) + 1;
// Make buf point to the full line buffer to parse.
if (!line_buf_.empty()) {
- line_buf_.append(buf);
- buf = const_cast<char*>(line_buf_.data());
+ line_buf_.append(buf, buf_len);
+ buf = line_buf_.data();
+ buf_len = static_cast<int>(line_buf_.size());
}
if (reached_last_chunk_) {
- if (*buf) {
+ if (buf_len) {
DLOG(INFO) << "ignoring http trailer";
} else {
reached_eof_ = true;
}
- } else if (*buf) {
+ } else if (chunk_terminator_remaining_) {
+ if (buf_len) {
+ DLOG(ERROR) << "chunk data not terminated properly";
+ return ERR_FAILED;
+ }
+ chunk_terminator_remaining_ = false;
+ } else if (buf_len) {
// Ignore any chunk-extensions.
- if ((p = strchr(buf, ';')) != NULL)
- *p = 0;
+ size_t index_of_semicolon = StringPiece(buf, buf_len).find(';');
+ if (index_of_semicolon != StringPiece::npos)
+ buf_len = static_cast<int>(index_of_semicolon);
- if (!sscanf_s(buf, "%x", &chunk_remaining_)) {
- DLOG(ERROR) << "sscanf failed parsing HEX from: " << buf;
+ if (!ParseChunkSize(buf, buf_len, &chunk_remaining_)) {
+ DLOG(ERROR) << "Failed parsing HEX from: " <<
+ std::string(buf, buf_len);
return ERR_FAILED;
}
if (chunk_remaining_ == 0)
reached_last_chunk_ = true;
- }
+ } else {
+ DLOG(ERROR) << "missing chunk-size";
+ return ERR_FAILED;
+ }
line_buf_.clear();
} else {
// Save the partial line; wait for more data.
- result = buf_len;
+ bytes_consumed = buf_len;
// Ignore a trailing CR
if (buf[buf_len - 1] == '\r')
@@ -130,7 +148,17 @@ int HttpChunkedDecoder::ScanForChunkRemaining(char* buf, int buf_len) {
line_buf_.append(buf, buf_len);
}
- return result;
+ return bytes_consumed;
+}
+
+// static
+bool HttpChunkedDecoder::ParseChunkSize(const char* start, int len, int* out) {
+ // Be more restrictive than HexStringToInt;
+ // don't allow inputs with leading "-", "+", "0x", "0X"
+ if (StringPiece(start, len).find_first_not_of("0123456789abcdefABCDEF")!=
+ StringPiece::npos)
+ return false;
+ return HexStringToInt(std::string(start, len), out);
}
} // namespace net