HTTP_RESULT HttpTcpConnection::chunkedRecv(char * buffer, size_t & bufsiz) { HTTP_RESULT res = HTTP_OK; // Read the chunked header // <size in hex> <chunk ext>\r\n //<data... upto size>\r\n // 0\r\n // \r\n unsigned int totalrecv = 0; int n; do { n = readChunkHeader(); if (n < 0) return HTTP_RECV_ERROR; if (n == 0) // terminate chunk { // Read the training \r\n bufsiz = totalrecv; consumeLine(); return res; } // terminate if we don't have enough // buffer to hold the data if ((totalrecv + n) > bufsiz) { bufsiz = totalrecv; return HTTP_RECV_BUFF_ERROR; } res = receive(buffer + totalrecv, (size_t &) n); if (res != HTTP_OK) { bufsiz = totalrecv + n; return res; } totalrecv += n; }while(totalrecv < bufsiz); return res; }
void HttpResponseParser::consume () { String line; while (true) { switch (mState){ case HP_START:{ if (mInputBuffer.empty()) return; bool suc = consumeLine (&line); if (!suc || mReady) return; if (sscanf (line.c_str(), "HTTP/1.0 %d", &mResponse->resultCode) == 1) { mResponse->httpVersion = HTTP_10; } else if (sscanf (line.c_str(), "HTTP/1.1 %d", &mResponse->resultCode) == 1) { mResponse->httpVersion = HTTP_11; } else { return fail (error::BadProtocol, "Bad protocol start"); } Log (LogInfo) << LOGID << "Response Code: " << mResponse->resultCode << " version= " << mResponse->httpVersion << std::endl; if (mResponse->resultCode == 100) { // it's a plain proceed, ignoring break; } mState = HP_INHEADERS; break; } case HP_INHEADERS:{ if (mInputBuffer.empty()) return; bool suc = consumeLine (&line); if (!suc || mReady) return; if (line.empty()) { mState = HP_INCONTENT; if (mResponse->headers.count("Content-Length") > 0){ String cl = mResponse->headers["Content-Length"]; try { mContentLength = boost::lexical_cast<size_t> (cl); mContentLengthSet = true; } catch (boost::bad_lexical_cast & e) { Log (LogInfo) << LOGID << "Could not cast content length" << std::endl; } } if (mResponse->headers.count("Transfer-Encoding") > 0){ String te = mResponse->headers["Transfer-Encoding"]; if (te == "chunked" || te == "Chunked" || te == "CHUNKED"){ mChunkedEncoding = true; mState = HP_INCONTENT_CHUNKBEGIN; } else { Log (LogWarning) << LOGID << "Unknown transfer encoding " << te << std::endl; } } if (!mContentLengthSet && !mChunkedEncoding) { return fail (error::BadProtocol, "Neither length set nor chunked encoding available, aborting"); } Log (LogInfo) << LOGID << "Finished with header" << std::endl; break; } String key, value; suc = splitHeader (line, &key, &value); if (!suc) { return fail (error::BadProtocol, "Strange protocol during header splitting"); } mResponse->headers[key] = value; // Log (LogInfo) << LOGID << "Parsed Header: " << key << " -> " << value << std::endl; break; } case HP_INCONTENT: if (mInputBuffer.size() < mContentLength){ // not enough, wait... return; } mResponse->data = createByteArrayPtr(); if (mInputBuffer.size() == mContentLength) { mResponse->data->swap(mInputBuffer); } else { mResponse->data->append(mInputBuffer.const_c_array(), mContentLength); mInputBuffer.l_truncate(mContentLength); } mReady = true; mResult = NoError; Log (LogInfo) << LOGID << "Ready, read " << mContentLength << std::endl; return; case HP_INCONTENT_CHUNKBEGIN:{ if (mInputBuffer.empty()) return; bool suc = consumeLine (&line); if (!suc) { return fail (error::BadProtocol, "Awaited a chunk line"); } // Log (LogInfo) << LOGID << "Read this as a chunk line: " << line << " len=" << line.size() << std::endl; if (line.empty()) break; // try it again // Chunk: Hexadecimal Length and then possiblly a ';' and some comment unsigned int l; int parsed = sscanf (line.c_str(), "%x", &l); if (parsed < 1) { return fail (error::BadProtocol, "Awaited hexadecimal chunk length"); } if (l == 0) { mState = HP_INFOOTERS; break; } // Log (LogInfo) << LOGID << "Len=" << l << std::endl; mContentLength = l; mState = HP_INCONTENT_CHUNK; break; } case HP_INCONTENT_CHUNK: { if (mInputBuffer.size() < mContentLength) return; // wait if (!mResponse->data) mResponse->data = createByteArrayPtr(); mResponse->data->append(mInputBuffer.c_array(), mContentLength); mInputBuffer.l_truncate(mContentLength); mState = HP_INCONTENT_CHUNKBEGIN; break; } case HP_INFOOTERS:{ bool suc = consumeLine (&line); if (!suc || mReady) return; if (line.empty()){ mReady = true; Log (LogInfo) << LOGID << "Ready." << std::endl; return; } Log (LogInfo) << LOGID << "Ignoring footer line: " << line << std::endl; break; } } } }