/* */ #include "ChunkedEncoding.h" #include "DlAbortEx.h" #include "message.h" #include "Util.h" #include "StringFormat.h" #include namespace aria2 { #define MAX_BUFSIZE (1024*1024) ChunkedEncoding::ChunkedEncoding() { strbufSize = 4096; strbuf = new unsigned char[strbufSize]; strbufTail = strbuf; state = READ_SIZE; chunkSize = 0; } ChunkedEncoding::~ChunkedEncoding() { delete [] strbuf; } void ChunkedEncoding::init() { } bool ChunkedEncoding::finished() { return state == FINISH ? true : false; } void ChunkedEncoding::end() {} void ChunkedEncoding::inflate(unsigned char* outbuf, size_t& outlen, const unsigned char* inbuf, size_t inlen) { addBuffer(inbuf, inlen); unsigned char* p = strbuf; size_t clen = 0; while(1) { if(state == READ_SIZE) { if(readChunkSize(&p) == 0) { if(chunkSize == 0) { state = FINISH; } else { state = READ_DATA; } } else { // chunk size is not fully received. break; } } else if(state == READ_DATA) { if(readData(&p, outbuf, clen, outlen) == 0) { state = READ_SIZE; } else { break; } } else { break; } // all bytes in strbuf were examined? if(strbufTail <= p) { break; } } if(strbufTail <= p) { strbufTail = strbuf; } else { // copy string between [p, strbufTail] size_t unreadSize = strbufTail-p; unsigned char* temp = new unsigned char[strbufSize]; memcpy(temp, p, unreadSize); delete [] strbuf; strbuf = temp; strbufTail = strbuf+unreadSize; } outlen = clen; } int ChunkedEncoding::readData(unsigned char** pp, unsigned char* buf, size_t& len, size_t maxlen) { if(buf+len == buf+maxlen) { return -1; } if(chunkSize == 0) { return readDataEOL(pp); } size_t wsize; if((size_t)(strbufTail-*pp) < chunkSize) { wsize = std::min((size_t)(strbufTail-*pp), maxlen-len); } else { wsize = std::min(chunkSize, maxlen-len); } memcpy(buf+len, *pp, wsize); chunkSize -= wsize; len += wsize; *pp += wsize; if(chunkSize == 0) { return readDataEOL(pp); } else { return -1; } } int ChunkedEncoding::readDataEOL(unsigned char** pp) { unsigned char* np = reinterpret_cast(memchr(*pp, '\n', strbufTail-*pp)); unsigned char* rp = reinterpret_cast(memchr(*pp, '\r', strbufTail-*pp)); if(np != NULL && rp != NULL && np-rp == 1 && *pp == rp) { *pp += 2; return 0; } else if(strbufTail-*pp < 2) { return -1; } else { throw DlAbortEx(EX_INVALID_CHUNK_SIZE); } } int ChunkedEncoding::readChunkSize(unsigned char** pp) { // we read chunk-size from *pp unsigned char* p; unsigned char* np = reinterpret_cast(memchr(*pp, '\n', strbufTail-*pp)); unsigned char* rp = reinterpret_cast(memchr(*pp, '\r', strbufTail-*pp)); if(np == NULL || rp == NULL || np-rp != 1) { // \r\n is not found. Return -1 return -1; } p = rp; // We ignore chunk-extension unsigned char* exsp = reinterpret_cast(memchr(*pp, ';', strbufTail-*pp)); if(exsp == 0 || p < exsp) { exsp = p; } std::string temp(*pp, exsp); chunkSize = Util::parseInt(temp, 16); if(chunkSize < 0) { throw DlAbortEx(EX_INVALID_CHUNK_SIZE); } *pp = p+2; return 0; } void ChunkedEncoding::addBuffer(const unsigned char* inbuf, size_t inlen) { size_t realbufSize = strbufTail-strbuf; if(realbufSize+inlen >= strbufSize) { if(realbufSize+inlen > MAX_BUFSIZE) { throw DlAbortEx (StringFormat(EX_TOO_LARGE_CHUNK, realbufSize+inlen).str()); } strbufSize = realbufSize+inlen; unsigned char* temp = new unsigned char[strbufSize]; memcpy(temp, strbuf, realbufSize); delete [] strbuf; strbuf = temp; strbufTail = strbuf+realbufSize; } memcpy(strbufTail, inbuf, inlen); strbufTail += inlen; } } // namespace aria2