svxlink/src/async/core/AsyncHttpServerConnection.cpp

427 lines
11 KiB
C++

/**
@file AsyncHttpServerConnection.cpp
@brief A TCP connection with framed instead of streamed content
@author Tobias Blomberg / SM0SVX
@date 2017-03-30
\verbatim
Async - A library for programming event driven applications
Copyright (C) 2003-2017 Tobias Blomberg / SM0SVX
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\endverbatim
*/
/****************************************************************************
*
* System Includes
*
****************************************************************************/
#include <cstring>
#include <cerrno>
#include <sstream>
/****************************************************************************
*
* Project Includes
*
****************************************************************************/
/****************************************************************************
*
* Local Includes
*
****************************************************************************/
#include "AsyncHttpServerConnection.h"
/****************************************************************************
*
* Namespaces to use
*
****************************************************************************/
using namespace std;
using namespace Async;
/****************************************************************************
*
* Defines & typedefs
*
****************************************************************************/
/****************************************************************************
*
* Local class definitions
*
****************************************************************************/
/****************************************************************************
*
* Prototypes
*
****************************************************************************/
/****************************************************************************
*
* Exported Global Variables
*
****************************************************************************/
/****************************************************************************
*
* Local Global Variables
*
****************************************************************************/
/****************************************************************************
*
* Public member functions
*
****************************************************************************/
HttpServerConnection::HttpServerConnection(size_t recv_buf_len)
: TcpConnection(recv_buf_len), m_state(STATE_DISCONNECTED)
{
TcpConnection::sendBufferFull.connect(
sigc::mem_fun(*this, &HttpServerConnection::onSendBufferFull));
} /* HttpServerConnection::HttpServerConnection */
HttpServerConnection::HttpServerConnection(
int sock, const IpAddress& remote_addr, uint16_t remote_port,
size_t recv_buf_len)
: TcpConnection(sock, remote_addr, remote_port, recv_buf_len),
m_state(STATE_EXPECT_START_LINE)
{
} /* HttpServerConnection::HttpServerConnection */
HttpServerConnection::~HttpServerConnection(void)
{
} /* HttpServerConnection::~HttpServerConnection */
void HttpServerConnection::disconnect(void)
{
//cout << "### HttpServerConnection::disconnect\n";
disconnectCleanup();
TcpConnection::disconnect();
} /* HttpServerConnection::disconnect */
//int HttpServerConnection::write(const void *buf, int count)
//{
// cout << "### HttpServerConnection::write: count=" << count << "\n";
// if (count < 0)
// {
// return 0;
// }
//
// //QueueItem *qi = new QueueItem(buf, count);
// //if (m_txq.empty())
// //{
// // int ret = TcpConnection::write(qi->m_buf, qi->m_size);
// // //cout << "### count=" << (qi->m_size-qi->m_pos) << " ret=" << ret << endl;
// // if (ret < 0)
// // {
// // delete qi;
// // qi = 0;
// // return -1;
// // }
// // if (ret >= qi->m_size)
// // {
// // delete qi;
// // qi = 0;
// // }
// // else
// // {
// // //cout << "### Not all bytes were sent: count=" << count
// // // << " ret=" << ret << endl;
// // qi->m_pos += ret;
// // }
// //}
//
// //if (qi != 0)
// //{
// // m_txq.push_back(qi);
// //}
//
// return count;
//} /* HttpServerConnection::write */
bool HttpServerConnection::write(const Response& res)
{
std::ostringstream os;
os << "HTTP/1.1 " << res.code() << " " << codeToString(res.code()) << "\r\n";
for (std::map<std::string, std::string>::const_iterator it=res.headers().begin();
it!=res.headers().end(); ++it)
{
os << (*it).first << ": " << (*it).second << "\r\n";
}
os << "\r\n";
if (res.sendContent())
{
os << res.content();
}
//std::cout << "### HttpServerConnection::write:" << std::endl;
//std::cout << os.str() << std::endl;
int len = TcpConnection::write(os.str().c_str(), os.str().size());
return len == static_cast<int>(os.str().size());
} /* HttpServerConnection::write */
/****************************************************************************
*
* Protected member functions
*
****************************************************************************/
void HttpServerConnection::onDisconnected(DisconnectReason reason)
{
//cout << "### HttpServerConnection::onDisconnected: "
// << TcpConnection::disconnectReasonStr(reason) << "\n";
disconnectCleanup();
disconnected(this, reason);
} /* HttpServerConnection::onDisconnected */
int HttpServerConnection::onDataReceived(void *buf, int count)
{
std::string data(reinterpret_cast<char*>(buf), count);
size_t data_pos = 0;
//std::cout << "### HttpServerConnection::onDataReceived: "
// << data << std::endl;
while (data.size() > data_pos)
{
if (m_state == STATE_EXPECT_START_LINE)
{
size_t eol = data.find("\r\n", data_pos);
size_t len = eol;
if (len != std::string::npos)
{
len -= data_pos;
}
m_row.append(data.substr(data_pos, len));
data_pos = eol;
if (eol != std::string::npos)
{
data_pos += 2;
handleStartLine();
m_row.clear();
}
}
else if (m_state == STATE_EXPECT_HEADER)
{
size_t eol = data.find("\r\n", data_pos);
size_t len = eol;
if (len != std::string::npos)
{
len -= data_pos;
}
m_row.append(data.substr(data_pos, len));
data_pos = eol;
if (eol != std::string::npos)
{
data_pos += 2;
handleHeader();
m_row.clear();
}
}
else
{
data_pos = std::string::npos;
//m_req.content.append(data);
}
}
if (m_state == STATE_REQ_COMPLETE)
{
requestReceived(this, m_req);
m_req.clear();
m_state = STATE_EXPECT_START_LINE;
}
return count;
} /* HttpServerConnection::onDataReceived */
/****************************************************************************
*
* Private member functions
*
****************************************************************************/
void HttpServerConnection::handleStartLine(void)
{
std::istringstream is(m_row);
std::string protocol;
if (!(is >> m_req.method >> m_req.target >> protocol >> std::ws))
{
std::cerr << "*** ERROR: Could not parse HTTP header" << std::endl;
disconnect();
return;
}
if (protocol.substr(0, 5) != "HTTP/")
{
std::cerr << "*** ERROR: Illegal protocol specification string \""
<< protocol << "\"" << std::endl;
disconnect();
return;
}
is.clear();
is.str(protocol.substr(5));
char dot;
if (!(is >> m_req.ver_major >> dot >> m_req.ver_minor >> std::ws) ||
(dot != '.'))
{
std::cerr << "*** ERROR: Illegal protocol version specification \""
<< protocol << "\"" << std::endl;
disconnect();
return;
}
//std::cout << "### HttpServerConnection::handleStartLine: method="
// << m_req.method << " target=" << m_req.target
// << " version=" << m_req.ver_major << "."
// << m_req.ver_minor
// << std::endl;
m_state = STATE_EXPECT_HEADER;
} /* HttpServerConnection::handleStartLine */
void HttpServerConnection::handleHeader(void)
{
//std::cout << "### HttpServerConnection::handleHeader: m_row="
// << m_row << std::endl;
if (m_row.empty())
{
m_state = STATE_REQ_COMPLETE;
return;
}
size_t colon = m_row.find(":");
if (colon == std::string::npos)
{
std::cerr << "*** ERROR: Malformed HTTP header received" << std::endl;
disconnect();
return;
}
std::string key(m_row.substr(0, colon));
size_t value_begin = m_row.find_first_not_of(" \t", colon+1);
size_t value_end = m_row.find_last_not_of(" \t");
if (value_begin == std::string::npos)
{
std::cerr << "*** ERROR: Malformed HTTP header value received" << std::endl;
disconnect();
return;
}
std::string value(m_row.substr(value_begin, value_end-value_begin+1));
//std::cout << "### HttpServerConnection::handleStartLine: key="
// << key << " value=" << value << std::endl;
m_req.headers[key] = value;
} /* HttpServerConnection::handleHeader */
void HttpServerConnection::onSendBufferFull(bool is_full)
{
cout << "### HttpServerConnection::onSendBufferFull: is_full="
<< is_full << "\n";
//if (!is_full)
//{
// while (!m_txq.empty())
// {
// QueueItem *qi = m_txq.front();
// int ret = TcpConnection::write(qi->m_buf+qi->m_pos, qi->m_size-qi->m_pos);
// //cout << "### count=" << (qi->m_size-qi->m_pos) << " ret=" << ret << endl;
// if (ret <= 0)
// {
// return;
// }
// qi->m_pos += ret;
// if (qi->m_pos < qi->m_size)
// {
// break;
// }
// m_txq.pop_front();
// delete qi;
// qi = 0;
// }
//}
} /* HttpServerConnection::onSendBufferFull */
void HttpServerConnection::disconnectCleanup(void)
{
//std::cout << "### HttpServerConnection::disconnectCleanup" << std::endl;
m_state = STATE_DISCONNECTED;
//for (TxQueue::iterator it = m_txq.begin(); it != m_txq.end(); ++it)
//{
// delete *it;
//}
//m_txq.clear();
} /* HttpServerConnection::disconnectCleanup */
const char* HttpServerConnection::codeToString(unsigned code)
{
switch (code)
{
case 200:
return "OK";
case 404:
return "Not Found";
case 501:
return "Not Implemented";
default:
return "?";
}
} /* HttpServerConnection::codeToString */
/*
* This file has not been truncated
*/