Esempio n. 1
0
bool http_servlet::doApp(acl::HttpServletRequest& req,
	acl::HttpServletResponse& res)
{
	acl::http_request* conn = (acl::http_request *) conns_->peek();
	if (conn == NULL)
	{
		logger_error("no connection available, addr: %s",
			var_cfg_manager_addr);
		return doReply(req, res, 500, "%s", "no connection");
	}

	acl::http_header& hdr = conn->request_header();
	hdr.set_url(var_cfg_access_url)
		.set_keep_alive(true)
		.set_method(acl::HTTP_METHOD_GET)
		.accept_gzip(true);

	if (conn->request(NULL, 0) == false)
	{
		logger_error("send request to %s error", var_cfg_manager_addr);
		conns_->put(conn, false);
		return doReply(req, res, 505, "%s", "send request error");
	}

	acl::xml1 xml;
	char buf[8192];
	while (true)
	{
		int ret = conn->read_body(buf, sizeof(buf) - 1);
		if (ret == 0)
			break;
		else if (ret < 0)
		{
			conns_->put(conn, false);
			logger_error("read_body error from %s",
				var_cfg_manager_addr);
			return doReply(req, res, 505, "%s", "read_body error");
		}

		buf[ret] = 0;
		xml.update(buf);
	}

	conns_->put(conn, true);

	acl::json json;
	if (xmlToJson(xml, json) == false)
		return doReply(req, res, 505, "%s", "invalid xml data");

	return doReply(req, res, 200, json);
}
/**
 * Network manager creates DHCPNAK
 */
int NetworkManager::nak(const Client& client, uint32_t u32Xid)
{

    Lease l = client.lease();
    if (l == Lease::NullLease)
        return VERR_INTERNAL_ERROR;

    prepareReplyPacket4Client(client, u32Xid);

    /* this field filed in prepareReplyPacket4Session, and
     * RFC 2131 require to have it zero fo NAK.
     */
    m->BootPReplyMsg.BootPHeader.bp_yiaddr.u = 0;

    /* options:
     * - message type (if DHCPREQUEST)
     * - server identifier
     */
    RawOption opt;
    std::vector<RawOption> extra;

    opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
    opt.au8RawOpt[0] = RTNET_DHCP_MT_NAC;
    opt.cbRawOpt = 1;
    extra.push_back(opt);

    return doReply(client, extra);
}
Esempio n. 3
0
bool http_servlet::doPost(acl::HttpServletRequest& req,
	acl::HttpServletResponse& res)
{
	// 如果需要 http session 控制,请打开下面注释,且需要保证
	// 在 master_service.cpp 的函数 thread_on_read 中设置的
	// memcached 服务正常工作
	/*
	const char* sid = req.getSession().getAttribute("sid");
	if (*sid == 0)
		req.getSession().setAttribute("sid", "xxxxxx");
	sid = req.getSession().getAttribute("sid");
	*/

	res.setCharacterEncoding("utf-8")		// 设置响应字符集
		.setKeepAlive(req.isKeepAlive())	// 设置是否保持长连接
		.setContentEncoding(false)		// 自动支持压缩传输
		.setChunkedTransferEncoding(false);	// 采用 chunk 传输方式

	acl::string path;
	path = req.getPathInfo();
	if (path.empty())
	{
		logger_error("getPathInfo NULL");
		return doReply(req, res, 400, "%s", "no PathInfo");
	}
	path.strip("..");
	if (path.empty())
	{
		logger_error("path empty");
		return doReply(req, res, 400, "%s", "path empty");
	}

	const std::vector<acl::string>& tokens = path.split2("/");
	// printf(">>>path: %s, size: %ld\r\n", path.c_str(), tokens.size());
	if (tokens.size() < 2 || !tokens[1].equal("website", false))
		return doApp(req, res);
	else
		return doDoc(req, res, path);
}
/**
 * Network manager creates DHCPACK
 */
int NetworkManager::ack(const Client& client, uint32_t u32Xid,
                        uint8_t *pu8ReqList, int cReqList)
{
    RTNETADDRIPV4 address;

    prepareReplyPacket4Client(client, u32Xid);

    Lease l = client.lease();
    address = l.getAddress();
    m->BootPReplyMsg.BootPHeader.bp_ciaddr =  address;


    /* rfc2131 4.3.1 is about DHCPDISCOVER and this value is equal to ciaddr from
     * DHCPREQUEST or 0 ...
     * XXX: Using addressHint is not correct way to initialize [cy]iaddress...
     */
    m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
    m->BootPReplyMsg.BootPHeader.bp_yiaddr = address;

    Assert(m->BootPReplyMsg.BootPHeader.bp_yiaddr.u);

    /* options:
     * - IP address lease time (if DHCPREQUEST)
     * - message type
     * - server identifier
     */
    RawOption opt;
    RT_ZERO(opt);

    std::vector<RawOption> extra;
    opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
    opt.au8RawOpt[0] = RTNET_DHCP_MT_ACK;
    opt.cbRawOpt = 1;
    extra.push_back(opt);

    /*
     * XXX: lease time should be conditional. If on dhcprequest then tim should be provided,
     * else on dhcpinform it mustn't.
     */
    opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
    *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(l.getExpiration());
    opt.cbRawOpt = sizeof(RTNETADDRIPV4);
    extra.push_back(opt);

    processParameterReqList(client, pu8ReqList, cReqList, extra);

    return doReply(client, extra);
}
Esempio n. 5
0
bool http_servlet::doUpload(acl::HttpServletRequest& req,
	acl::HttpServletResponse& res)
{
	// 获得输入流
	acl::istream& in = req.getInputStream();
	acl::string buf;
	bool  finish = false;

	//logger(">>>>>>>>>>read: %lld, total: %lld<<<<<",
	//	read_length_, content_length_);

	// 读取 HTTP 客户端请求数据
	while (content_length_ > read_length_)
	{
		if (in.read_peek(buf, true) == false)
			break;
		//if (buf.empty())
		//	break;
//		printf(">>>size: %ld, space: %ld\r\n",
//			(long) buf.size(), (long) buf.capacity());

		if (fp_.write(buf) == -1)
		{
			logger_error("write error %s", acl::last_serror());
			(void) doReply(req, res, "write error");
			return false;
		}

		read_length_ += buf.size();

		// 将读得到的数据输入至解析器进行解析
		if (!finish && mime_->update(buf, buf.size()) == true)
			finish = true;
	}

	if (in.eof())
	{
		logger_error("read error");
		return false;
	}

	return true;
}
/**
 * Network manager creates DHCPOFFER datagramm
 */
int NetworkManager::offer4Client(const Client& client, uint32_t u32Xid,
                                 uint8_t *pu8ReqList, int cReqList)
{
    Lease l(client); /* XXX: oh, it looks badly, but now we have lease */
    prepareReplyPacket4Client(client, u32Xid);

    RTNETADDRIPV4 address = l.getAddress();
    m->BootPReplyMsg.BootPHeader.bp_yiaddr =  address;

    /* Ubuntu ???*/
    m->BootPReplyMsg.BootPHeader.bp_ciaddr =  address;

    /* options:
     * - IP lease time
     * - message type
     * - server identifier
     */
    RawOption opt;
    RT_ZERO(opt);

    std::vector<RawOption> extra;
    opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
    opt.au8RawOpt[0] = RTNET_DHCP_MT_OFFER;
    opt.cbRawOpt = 1;
    extra.push_back(opt);

    opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;

    const NetworkConfigEntity *pCfg = l.getConfig();
    AssertPtr(pCfg);

    *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(pCfg->expirationPeriod());
    opt.cbRawOpt = sizeof(RTNETADDRIPV4);

    extra.push_back(opt);

    processParameterReqList(client, pu8ReqList, cReqList, extra);

    return doReply(client, extra);
}
Esempio n. 7
0
bool http_servlet::doDoc(acl::HttpServletRequest& req,
	acl::HttpServletResponse& res, const char* path)
{
	acl::string filepath(var_cfg_home_path);

	if (*(var_cfg_home_path + strlen(var_cfg_home_path) - 1) != '/'
		&& *path != '/')
	{
		filepath << '/';
	}

	filepath << path;

	if (*(path + strlen(path) - 1) == '/')
		filepath << "index.html";

	acl::ifstream in;
	if (in.open_read(filepath) == false)
	{
		logger_error("open %s error %s", filepath.c_str(),
			acl::last_serror());
		return doReply(req, res, 404, "%s", "Not found");
	}
	//printf("---open %s ok---\r\n", filepath.c_str());

	long long len = in.fsize();
	if (len <= 0)
	{
		logger_error("invalid fisze: %lld, file: %s",
			len, filepath.c_str());
		return doReply(req, res, 500, "%s", "Can't get file size");
	}
	res.setContentLength(len);

	acl::string ctype;
	getContentType(filepath, ctype);
	res.setContentType(ctype);

	char buf[8192];
	int  ret;
	long long n = 0;

	while (!in.eof())
	{
		if ((ret = in.read(buf, sizeof(buf), false)) == -1)
		{
			//logger_error("read from %s error %s", filepath.c_str(),
			//	acl::last_serror());
			break;
		}
		if (res.write(buf, ret) == false)
		{
			logger_error("write to client error, file %s",
				filepath.c_str());
			return false;
		}

		n += ret;
	}

	if (n != len)
	{
		logger_error("write length(%lld) != file size(%lld), file: %s",
			n, len, filepath.c_str());
		return false;
	}

	return res.write(NULL, 0);
}
void HttpRequestAndResponsePacketTF::testDecodeAndEncode() {
    HttpRequestPacket *request = new HttpRequestPacket;
    HttpResponsePacket *response;
    DataBuffer output;
    DataBuffer input;
    PacketHeader header;

    // http_load -r 1 -f 2 url
    char s1[] = "GET /  HTTP/1.0\r\n"
                "Host: localhost\r\n"
                "User-Agent: http_load 12mar2006\r\n\r\n";
    header._dataLen = sizeof(s1)-1;
    input.writeBytes(s1, sizeof(s1) - 1);
    CPPUNIT_ASSERT(request->decode(&input, &header));
    CPPUNIT_ASSERT_EQUAL(1, request->_method);
    CPPUNIT_ASSERT_EQUAL(1, request->_version);
    CPPUNIT_ASSERT_EQUAL(false, request->isKeepAlive());
    CPPUNIT_ASSERT(strcmp("localhost", request->findHeader("Host")) == 0);
    CPPUNIT_ASSERT(request->findHeader("User-Agent"));
    CPPUNIT_ASSERT(strcmp("http_load 12mar2006", request->findHeader("User-Agent")) == 0);

    response = doReply(request);
    response->encode(&output);
    char r1[] = "HTTP/1.1 200 OK\r\n"
                "Connection: close\r\n"
                "Content-Type: text/html\r\n"
                "Content-Length: 1\r\n\r\n/";
    CPPUNIT_ASSERT(strncmp(r1, output.getData(), sizeof(r1) - 1) == 0);
    output.clear();
    delete response;
    request->free();

    request = new HttpRequestPacket;
    //fbench -q url -c 0  -n 15 -k -s 10 localhost 12345
    request->_isKeepAlive = true;
    char s2[] = "GET http://localhost:12345?name=huang&jj=yy HTTP/1.1\r\n"
                "Host: localhost\r\n"
                "User-Agent: fbench/0.9\r\n\r\n";
    header._dataLen = sizeof(s2)-1;
    input.writeBytes(s2, sizeof(s2) - 1);
    CPPUNIT_ASSERT(request->decode(&input, &header));
    CPPUNIT_ASSERT_EQUAL(1, request->_method);
    CPPUNIT_ASSERT_EQUAL(2, request->_version);
    CPPUNIT_ASSERT_EQUAL(true, request->isKeepAlive());
    CPPUNIT_ASSERT(strcmp("localhost", request->findHeader("Host")) == 0);
    CPPUNIT_ASSERT(strcmp("fbench/0.9", request->findHeader("User-Agent")) == 0);
    CPPUNIT_ASSERT(strcmp(request->getQuery(), "http://localhost:12345?name=huang&jj=yy") == 0);

    response = doReply(request);
    response->encode(&output);
    char r2[] = "HTTP/1.1 200 OK\r\n"
                "Connection: Keep-Alive\r\n"
                "Keep-Alive: timeout=10, max=10\r\n"
                "Content-Type: text/html\r\n"
                "Content-Length: 39\r\n\r\n"
                "http://localhost:12345?name=huang&jj=yy";
    CPPUNIT_ASSERT(strncmp(r2, output.getData(), sizeof(r2) - 1) == 0);
    output.clear();
    delete response;
    request->free();

    request = new HttpRequestPacket;
    //fbench -q url -c 0  -n 15 -s 10 localhost 12345
    char s3[] = "GET http://localhost:12345?name=huang&jj=yy HTTP/1.1\r\n"
                "Host: localhost\r\n"
                "Connection: close\r\n"
                "User-Agent: fbench/0.9\r\n\r\n";
    header._dataLen = sizeof(s3)-1;
    input.writeBytes(s3, sizeof(s3) - 1);
    CPPUNIT_ASSERT(request->decode(&input, &header));
    CPPUNIT_ASSERT_EQUAL(1, request->_method);
    CPPUNIT_ASSERT_EQUAL(2, request->_version);
    CPPUNIT_ASSERT_EQUAL(false, request->isKeepAlive());
    CPPUNIT_ASSERT(strcmp("localhost", request->findHeader("Host")) == 0);
    CPPUNIT_ASSERT(strcmp("fbench/0.9", request->findHeader("User-Agent")) == 0);
    CPPUNIT_ASSERT(strcmp(request->getQuery(), "http://localhost:12345?name=huang&jj=yy") == 0);

    response = doReply(request);
    response->encode(&output);
    char r3[] = "HTTP/1.1 200 OK\r\n"
                "Connection: close\r\n"
                "Content-Type: text/html\r\n"
                "Content-Length: 39\r\n\r\n"
                "http://localhost:12345?name=huang&jj=yytp://localhost:12345?name=huang&jj=yy";
    CPPUNIT_ASSERT(strncmp(r3, output.getData(), sizeof(r3) - 1) == 0);
    output.clear();
    delete response;
    request->free();

    request = new HttpRequestPacket;
    //firefox
    request->_isKeepAlive = true;
    char s4[] ="GET /?name=huang&jj=yy HTTP/1.0\r\n"
               "Host: localhost:12345\r\n"
               "User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9b5) Gecko/2008042803 Red Hat/3.0b5-0.beta5.6.el5 Firefox/3.0b5\r\n"
               "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
               "Accept-Language: en-us,en;q=0.5\r\n"
               "Accept-Encoding: gzip,deflate\r\n"
               "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
               "Keep-Alive: 300\r\n"
               "Connection: keep-alive\r\n\r\n";
    header._dataLen = sizeof(s4)-1;
    input.writeBytes(s4, sizeof(s4) - 1);
    CPPUNIT_ASSERT(request->decode(&input, &header));
    CPPUNIT_ASSERT_EQUAL(1, request->_method);
    CPPUNIT_ASSERT_EQUAL(1, request->_version);
    CPPUNIT_ASSERT_EQUAL(true, request->isKeepAlive());
    CPPUNIT_ASSERT(strcmp("localhost:12345", request->findHeader("Host")) == 0);
    CPPUNIT_ASSERT(strcmp("Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9b5) Gecko/2008042803 Red Hat/3.0b5-0.beta5.6.el5 Firefox/3.0b5",
                          request->findHeader("User-Agent")) == 0);
    CPPUNIT_ASSERT(strcmp(request->getQuery(), "/?name=huang&jj=yy") == 0);

    response = doReply(request);
    response->encode(&output);
    char r4[] = "HTTP/1.1 200 OK\r\n"
                "Connection: Keep-Alive\r\n"
                "Keep-Alive: timeout=10, max=10\r\n"
                "Content-Type: text/html\r\n"
                "Content-Length: 18\r\n\r\n"
                "/?name=huang&jj=yy2345?name=huang&jj=yy";
    CPPUNIT_ASSERT(strncmp(r4, output.getData(), sizeof(r4) - 1) == 0);
    output.clear();
    delete response;
    request->free();

    request = new HttpRequestPacket;
    // http_load -r 1 -f 2 url keep-alive:true 
    request->_isKeepAlive = true;
    char s5[] = "GET / HTTP/1.0\r\n"
                "Host: localhost\r\n"
                "Connection: keep-alive\r\n"
                "User-Agent: http_load 12mar2006\r\n\r\n";
    header._dataLen = sizeof(s5)-1;
    input.writeBytes(s5, sizeof(s5) - 1);
    CPPUNIT_ASSERT(request->decode(&input, &header));
    CPPUNIT_ASSERT_EQUAL(1, request->_method);
    CPPUNIT_ASSERT_EQUAL(1, request->_version);
    CPPUNIT_ASSERT_EQUAL(true, request->isKeepAlive());
    CPPUNIT_ASSERT(strcmp("localhost", request->findHeader("Host")) == 0);
    CPPUNIT_ASSERT(request->findHeader("User-Agent"));
    CPPUNIT_ASSERT(strcmp("http_load 12mar2006", request->findHeader("User-Agent")) == 0);

    response = doReply(request);
    response->encode(&output);
    cout<<output.getData();
    char r5[] = "HTTP/1.1 200 OK\r\n"
                "Connection: Keep-Alive\r\n"
                "Keep-Alive: timeout=10, max=10\r\n"
                "Content-Type: text/html\r\n"
                "Content-Length: 1\r\n\r\n"
                "//?name=huang&jj=yy2345?name=huang&jj=yy";
    CPPUNIT_ASSERT(strncmp(r5, output.getData(), sizeof(r5) - 1) == 0);
    output.clear();
    delete response;
    request->free();

    request = new HttpRequestPacket;
    //wrong query 
    request->_isKeepAlive = true;
    char s6[] = "GET  HTTP/1.0\r\n"
                "Host: localhost\r\n"
                "Connection: keep-alive\r\n";
    header._dataLen = sizeof(s6)-1;
    input.writeBytes(s6, sizeof(s6) - 1);
    CPPUNIT_ASSERT(!request->decode(&input, &header));
    request->free();
}   
Esempio n. 9
0
bool http_servlet::doPost(acl::HttpServletRequest& req,
	acl::HttpServletResponse& res)
{
	res.setContentType("text/xml; charset=gbk")	// 设置响应字符集
		.setKeepAlive(req.isKeepAlive())	// 设置是否保持长连接
		.setContentEncoding(true)		// 自动支持压缩传输
		.setChunkedTransferEncoding(true);	// 采用 chunk 传输方式

	// 获得 HTTP 请求的数据类型,正常的参数类型,即 name&value 方式
	// 还是 MIME 数据类型,还是数据流类型
	acl::http_request_t request_type = req.getRequestType();
	if (request_type != acl::HTTP_REQUEST_MULTIPART_FORM)
	{
		acl::string buf;
		buf.format("<root error='should acl::HTTP_REQUEST_MULTIPART_FORM' />\r\n");
		(void) res.write(buf);
		(void) res.write(NULL, 0);
		return false;
	}

	// 先获得 Content-Type 对应的 http_ctype 对象
	mime_ = req.getHttpMime();
	if (mime_ == NULL)
	{
		logger_error("http_mime null");
		(void) doReply(req, res, "http_mime null");
		return false;
	}

	// 获得数据体的长度
	content_length_ = req.getContentLength();
	if (content_length_ <= 0)
	{
		logger_error("body empty");
		(void) doReply(req, res, "body empty");
		return false;
	}

	acl::string filepath;
#if defined(_WIN32) || defined(_WIN64)
	filepath.format("%s\\mime_file", var_cfg_var_path);
#else
	filepath.format("%s/mime_file", var_cfg_var_path);
#endif

	if (fp_.open_write(filepath) == false)
	{
		logger_error("open %s error %s",
			filepath.c_str(), acl::last_serror());
		(void) doReply(req, res, "open file error");
		return false;
	}

	// 设置原始文件存入路径
	mime_->set_saved_path(filepath);

	req_ = &req;
	res_ = &res;
	read_body_ = true;

	// 直接返回,从而触发异步读 HTTP 数据体过程
	return true;
}
Esempio n. 10
0
bool http_servlet::doParse(acl::HttpServletRequest& req,
	acl::HttpServletResponse& res)
{
	const char* ptr = req.getParameter("name1");
	if (ptr)
		param1_ = ptr;
	ptr = req.getParameter("name2");
	if (ptr)
		param2_ = ptr;
	ptr = req.getParameter("name3");
	if (ptr)
		param3_ = ptr;

	acl::string path;

	// 遍历所有的 MIME 结点,找出其中为文件结点的部分进行转储
	const std::list<acl::http_mime_node*>& nodes = mime_->get_nodes();
	std::list<acl::http_mime_node*>::const_iterator cit = nodes.begin();
	for (; cit != nodes.end(); ++cit)
	{
		const char* name = (*cit)->get_name();
		if (name == NULL)
			continue;

		acl::http_mime_t mime_type = (*cit)->get_mime_type();
		if (mime_type == acl::HTTP_MIME_FILE)
		{
			const char* filename = (*cit)->get_filename();
			if (filename == NULL)
			{
				logger("filename null");
				continue;
			}

			// 有的浏览器(如IE)上传文件时会带着文件路径,所以
			// 需要先将路径去掉
			filename = acl_safe_basename(filename);
#if defined(_WIN32) || defined(_WIN64)
			path.format("%s\\%s", var_cfg_var_path, filename);
#else
			path.format("%s/%s", var_cfg_var_path, filename);
#endif
			(void) (*cit)->save(path.c_str());

			if (strcmp(name, "file1") == 0)
			{
				file1_ = filename;
				fsize1_ = get_fsize(var_cfg_var_path, filename);
			}
			else if (strcmp(name, "file2") == 0)
			{
				file2_ = filename;
				fsize2_ = get_fsize(var_cfg_var_path, filename);
			}
			else if (strcmp(name, "file3") == 0)
			{
				file3_ = filename;
				fsize3_ = get_fsize(var_cfg_var_path, filename);
			}
		}
	}

	// 查找上载的某个文件并转储
	const acl::http_mime_node* node = mime_->get_node("file1");
	if (node && node->get_mime_type() == acl::HTTP_MIME_FILE)
	{
		ptr = node->get_filename();
		if (ptr)
		{
			// 有的浏览器(如IE)上传文件时会带着文件路径,所以
			// 需要先将路径去掉
			ptr = acl_safe_basename(ptr);
#if defined(_WIN32) || defined(_WIN64)
			path.format("%s\\1_%s", var_cfg_var_path, ptr);
#else
			path.format("%s/1_%s", var_cfg_var_path, ptr);
#endif
			(void) node->save(path.c_str());
		}
	}

	return doReply(req, res, "OK");
}
Esempio n. 11
0
void Socket::onInput(StreamBuffer& sb)
{
    log_debug("onInput");

    sb.endRead();

    if (sb.in_avail() == 0 || sb.device()->eof())
    {
        close();
        return;
    }

    _timer.start(_server.readTimeout());
    if ( _responder == 0 )
    {
        _parser.advance(sb);

        if (_parser.fail())
        {
            _responder = _server.getDefaultResponder(_request);
            _responder->replyError(_reply.body(), _request, _reply,
                std::runtime_error("invalid http header"));
            _responder->release();
            _responder = 0;

            sendReply();

            onOutput(sb);
            return;
        }

        if (_parser.end())
        {
            log_info("request " << _request.method() << ' ' << _request.header().query()
                << " from client " << getPeerAddr());
            _responder = _server.getResponder(_request);
            try
            {
                _responder->beginRequest(_stream, _request);
            }
            catch (const std::exception& e)
            {
                _reply.setHeader("Connection", "close");
                _responder->replyError(_reply.body(), _request, _reply, e);
                _responder->release();
                _responder = 0;
                sendReply();

                onOutput(sb);
                return;
            }

            _contentLength = _request.header().contentLength();
            log_debug("content length of request is " << _contentLength);
            if (_contentLength == 0)
            {
                _timer.stop();
                doReply();
                return;
            }

        }
        else
        {
            sb.beginRead();
        }
    }

    if (_responder)
    {
        if (sb.in_avail() > 0)
        {
            try
            {
                std::size_t s = _responder->readBody(_stream);
                assert(s > 0);
                _contentLength -= s;
            }
            catch (const std::exception& e)
            {
                _reply.setHeader("Connection", "close");
                _responder->replyError(_reply.body(), _request, _reply, e);
                _responder->release();
                _responder = 0;
                sendReply();

                onOutput(sb);
                return;
            }
        }

        if (_contentLength <= 0)
        {
            _timer.stop();
            doReply();
        }
        else
        {
            sb.beginRead();
        }
    }
}