Example #1
0
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;
			}
		}
	}
}