#ifdef _MSC_VER
#include "stdafx.h"
#else
#include "config.h"
#include <poll.h>
#endif
#include "HtmlBuilder.h"
#include "HttpConnection.h"
#include "HttpMessage.h"
#include "Session.h"
#include "Utility.h"
using namespace HTML;
namespace FIX {
HttpConnection::HttpConnection(socket_handle s)
: m_socket(s) {
#ifdef _MSC_VER
FD_ZERO(&m_fds);
FD_SET(m_socket, &m_fds);
#endif
}
bool HttpConnection::send(const std::string &msg) { return socket_send(m_socket, msg.c_str(), msg.length()) >= 0; }
void HttpConnection::disconnect(int error) {
if (error > 0) {
send(HttpMessage::createResponse(error));
}
socket_close(m_socket);
}
bool HttpConnection::read() {
#if _MSC_VER
struct timeval timeout = {2, 0};
fd_set readset = m_fds;
#else
int timeout = 2000; struct pollfd pfd = {m_socket, POLLIN | POLLPRI, 0};
#endif
try {
#if _MSC_VER
int result = select(0, &readset, 0, 0, &timeout);
#else
int result = poll(&pfd, 1, timeout);
#endif
if (result > 0) {
ssize_t size = socket_recv(m_socket, m_buffer, sizeof(m_buffer));
if (size <= 0) {
throw SocketRecvFailed(size);
}
m_parser.addToStream(m_buffer, size);
} else if (result == 0) {
disconnect(408);
return false;
} else if (result < 0) {
throw SocketRecvFailed(result);
}
processStream();
return true;
} catch (SocketRecvFailed &) {
disconnect();
return false;
}
}
bool HttpConnection::readMessage(std::string &msg) EXCEPT(SocketRecvFailed) {
try {
return m_parser.readHttpMessage(msg);
} catch (MessageParseError &) {
disconnect(400);
}
return true;
}
void HttpConnection::processStream() {
std::string msg;
try {
if (!readMessage(msg)) {
return;
}
HttpMessage request(msg);
processRequest(request);
} catch (InvalidMessage &) {
disconnect(400);
return;
}
return;
}
void HttpConnection::processRequest(const HttpMessage &request) {
int error = 200;
std::stringstream h;
std::stringstream b;
std::string titleString = "QuickFIX Engine Web Interface";
{
HEAD head(h);
head.text();
{
CENTER center(h);
center.text();
{
TITLE title(h);
title.text(titleString);
}
{
H1 h1(h);
h1.text(titleString);
}
}
{
CENTER center(h);
center.text();
{
A a(h);
a.href("/").text("HOME");
}
h << NBSP;
{
A a(h);
a.href(request.toString()).text("RELOAD");
}
}
HR hr(h);
hr.text();
}
BODY body(b);
body.text();
try {
if (request.getRootString() == "/") {
processRoot(request, h, b);
} else if (request.getRootString() == "/resetSessions") {
processResetSessions(request, h, b);
} else if (request.getRootString() == "/refreshSessions") {
processRefreshSessions(request, h, b);
} else if (request.getRootString() == "/enableSessions") {
processEnableSessions(request, h, b);
} else if (request.getRootString() == "/disableSessions") {
processDisableSessions(request, h, b);
} else if (request.getRootString() == "/session") {
processSession(request, h, b);
} else if (request.getRootString() == "/resetSession") {
processResetSession(request, h, b);
} else if (request.getRootString() == "/refreshSession") {
processRefreshSession(request, h, b);
} else {
error = 404;
}
} catch (std::exception &e) {
error = 400;
b << e.what();
}
std::string response = "<HTML>" + h.str() + b.str() + "</HTML>";
send(HttpMessage::createResponse(error, error == 200 ? response : ""));
disconnect();
}
void HttpConnection::processRoot(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
TABLE table(b);
table.border(1).cellspacing(2).width(100).text();
{
CAPTION caption(b);
caption.text();
EM em(b);
em.text();
b << Session::numSessions() << " Sessions managed by QuickFIX";
{
HR hr(b);
hr.text();
}
{
b << NBSP;
A a(b);
a.href("/resetSessions" + request.getParameterString()).text("RESET");
}
{
b << NBSP;
A a(b);
a.href("/refreshSessions" + request.getParameterString()).text("REFRESH");
}
{
b << NBSP;
A a(b);
a.href("/enableSessions" + request.getParameterString()).text("ENABLE");
}
{
b << NBSP;
A a(b);
a.href("/disableSessions" + request.getParameterString()).text("DISABLE");
}
}
{
TR tr(b);
tr.text();
{
TD td(b);
td.align("center").text("Session");
}
{
TD td(b);
td.align("center").text("ConnectionType");
}
{
TD td(b);
td.align("center").text("Enabled");
}
{
TD td(b);
td.align("center").text("Session Time");
}
{
TD td(b);
td.align("center").text("Logged On");
}
{
TD td(b);
td.align("center").text("Next Incoming");
}
{
TD td(b);
td.align("center").text("Next Outgoing");
}
}
std::set<SessionID> sessions = Session::getSessions();
for (const SessionID &sessionID : sessions) {
Session *pSession = Session::lookupSession(sessionID);
if (!pSession) {
continue;
}
{
TR tr(b);
tr.text();
{
TD td(b);
td.text();
std::string href = "/session?BeginString=" + sessionID.getBeginString().getValue()
+ "&SenderCompID=" + sessionID.getSenderCompID().getValue()
+ "&TargetCompID=" + sessionID.getTargetCompID().getValue();
if (sessionID.getSessionQualifier().size()) {
href += "&SessionQualifier=" + sessionID.getSessionQualifier();
}
A a(b);
a.href(href).text(sessionID.toString());
}
{
TD td(b);
td.text(pSession->isInitiator() ? "initiator" : "acceptor");
}
{
TD td(b);
td.text(pSession->isEnabled() ? "yes" : "no");
}
{
TD td(b);
td.text(pSession->isSessionTime(UtcTimeStamp::now()) ? "yes" : "no");
}
{
TD td(b);
td.text(pSession->isLoggedOn() ? "yes" : "no");
}
{
TD td(b);
td.text(pSession->getExpectedTargetNum());
}
{
TD td(b);
td.text(pSession->getExpectedSenderNum());
}
}
}
}
void HttpConnection::processResetSessions(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
try {
HttpMessage copy = request;
bool confirm = false;
if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
confirm = true;
std::set<SessionID> sessions = Session::getSessions();
std::set<SessionID>::iterator session;
for (session = sessions.begin(); session != sessions.end(); ++session) {
Session::lookupSession(*session)->reset();
}
copy.removeParameter("confirm");
}
if (confirm) {
h << "<META http-equiv='refresh' content=2;URL='" << "/'>";
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
{
A a(b);
a.href("/").text("Sessions");
}
b << " have been reset";
} else {
{
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
b << "Are you sure you want to reset all sessions ?";
}
{
CENTER center(b);
center.text();
b << "[";
{
A a(b);
a.href(request.toString() + "?confirm=1").text("YES, reset sessions");
}
b << "]" << NBSP << "[";
{
A a(b);
a.href("/").text("NO, do not reset sessions");
}
b << "]";
}
}
} catch (std::exception &e) {
b << e.what();
}
}
void HttpConnection::processRefreshSessions(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
try {
HttpMessage copy = request;
bool confirm = false;
if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
confirm = true;
std::set<SessionID> sessions = Session::getSessions();
for (const SessionID &sessionID : sessions) {
Session::lookupSession(sessionID)->refresh();
}
copy.removeParameter("confirm");
}
if (confirm) {
h << "<META http-equiv='refresh' content=2;URL='" << "/'>";
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
{
A a(b);
a.href("/").text("Sessions");
}
b << " have been refreshed";
} else {
{
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
b << "Are you sure you want to refresh all sessions ?";
}
{
CENTER center(b);
center.text();
b << "[";
{
A a(b);
a.href(request.toString() + "?confirm=1").text("YES, refresh sessions");
}
b << "]" << NBSP << "[";
{
A a(b);
a.href("/").text("NO, do not refresh sessions");
}
b << "]";
}
}
} catch (std::exception &e) {
b << e.what();
}
}
void HttpConnection::processEnableSessions(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
try {
HttpMessage copy = request;
bool confirm = false;
if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
confirm = true;
std::set<SessionID> sessions = Session::getSessions();
for (const SessionID &sessionID : sessions) {
Session::lookupSession(sessionID)->logon();
}
copy.removeParameter("confirm");
}
if (confirm) {
h << "<META http-equiv='refresh' content=2;URL='" << "/'>";
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
{
A a(b);
a.href("/").text("Sessions");
}
b << " have been enabled";
} else {
{
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
b << "Are you sure you want to enable all sessions ?";
}
{
CENTER center(b);
center.text();
b << "[";
{
A a(b);
a.href(request.toString() + "?confirm=1").text("YES, enable sessions");
}
b << "]" << NBSP << "[";
{
A a(b);
a.href("/").text("NO, do not enable sessions");
}
b << "]";
}
}
} catch (std::exception &e) {
b << e.what();
}
}
void HttpConnection::processDisableSessions(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
try {
HttpMessage copy = request;
bool confirm = false;
if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
confirm = true;
std::set<SessionID> sessions = Session::getSessions();
for (const SessionID &sessionID : sessions) {
Session::lookupSession(sessionID)->logout();
}
copy.removeParameter("confirm");
}
if (confirm) {
h << "<META http-equiv='refresh' content=2;URL='" << "/'>";
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
{
A a(b);
a.href("/").text("Sessions");
}
b << " have been disabled";
} else {
{
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
b << "Are you sure you want to disable all sessions ?";
}
{
CENTER center(b);
center.text();
b << "[";
{
A a(b);
a.href(request.toString() + "?confirm=1").text("YES, disable sessions");
}
b << "]" << NBSP << "[";
{
A a(b);
a.href("/").text("NO, do not disable sessions");
}
b << "]";
}
}
} catch (std::exception &e) {
b << e.what();
}
}
void HttpConnection::processSession(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
try {
HttpMessage copy = request;
std::string url = request.toString();
std::string beginString = copy.getParameter("BeginString");
std::string senderCompID = copy.getParameter("SenderCompID");
std::string targetCompID = copy.getParameter("TargetCompID");
std::string sessionQualifier;
if (copy.hasParameter("SessionQualifier")) {
sessionQualifier = copy.getParameter("SessionQualifier");
}
SessionID sessionID(beginString, senderCompID, targetCompID, sessionQualifier);
Session *pSession = Session::lookupSession(sessionID);
if (pSession == 0) {
throw SessionNotFound();
}
if (copy.hasParameter("Enabled")) {
copy.getParameter("Enabled") == "0" ? pSession->logout() : pSession->logon();
copy.removeParameter("Enabled");
}
if (copy.hasParameter("Next%20Incoming")) {
int value = IntConvertor::convert(copy.getParameter("Next%20Incoming"));
pSession->setNextTargetMsgSeqNum(value <= 0 ? 1 : value);
copy.removeParameter("Next%20Incoming");
}
if (copy.hasParameter("Next%20Outgoing")) {
int value = IntConvertor::convert(copy.getParameter("Next%20Outgoing"));
pSession->setNextSenderMsgSeqNum(value <= 0 ? 1 : value);
copy.removeParameter("Next%20Outgoing");
}
if (copy.hasParameter(SEND_REDUNDANT_RESENDREQUESTS)) {
pSession->setSendRedundantResendRequests(copy.getParameter(SEND_REDUNDANT_RESENDREQUESTS) != "0");
copy.removeParameter(SEND_REDUNDANT_RESENDREQUESTS);
}
if (copy.hasParameter(CHECK_COMPID)) {
pSession->setCheckCompId(copy.getParameter(CHECK_COMPID) != "0");
copy.removeParameter(CHECK_COMPID);
}
if (copy.hasParameter(CHECK_LATENCY)) {
pSession->setCheckLatency(copy.getParameter(CHECK_LATENCY) != "0");
copy.removeParameter(CHECK_LATENCY);
}
if (copy.hasParameter(MAX_LATENCY)) {
int value = IntConvertor::convert(copy.getParameter(MAX_LATENCY));
pSession->setMaxLatency(value <= 0 ? 1 : value);
copy.removeParameter(MAX_LATENCY);
}
if (copy.hasParameter(LOGON_TIMEOUT)) {
int value = IntConvertor::convert(copy.getParameter(LOGON_TIMEOUT));
pSession->setLogonTimeout(value <= 0 ? 1 : value);
copy.removeParameter(LOGON_TIMEOUT);
}
if (copy.hasParameter(LOGOUT_TIMEOUT)) {
int value = IntConvertor::convert(copy.getParameter(LOGOUT_TIMEOUT));
pSession->setLogoutTimeout(value <= 0 ? 1 : value);
copy.removeParameter(LOGOUT_TIMEOUT);
}
if (copy.hasParameter(RESET_ON_LOGON)) {
pSession->setResetOnLogon(copy.getParameter(RESET_ON_LOGON) != "0");
copy.removeParameter(RESET_ON_LOGON);
}
if (copy.hasParameter(RESET_ON_LOGOUT)) {
pSession->setResetOnLogout(copy.getParameter(RESET_ON_LOGOUT) != "0");
copy.removeParameter(RESET_ON_LOGOUT);
}
if (copy.hasParameter(RESET_ON_DISCONNECT)) {
pSession->setResetOnDisconnect(copy.getParameter(RESET_ON_DISCONNECT) != "0");
copy.removeParameter(RESET_ON_DISCONNECT);
}
if (copy.hasParameter(REFRESH_ON_LOGON)) {
pSession->setRefreshOnLogon(copy.getParameter(REFRESH_ON_LOGON) != "0");
copy.removeParameter(REFRESH_ON_LOGON);
}
if (copy.hasParameter(MILLISECONDS_IN_TIMESTAMP)) {
pSession->setMillisecondsInTimeStamp(copy.getParameter(MILLISECONDS_IN_TIMESTAMP) != "0");
copy.removeParameter(MILLISECONDS_IN_TIMESTAMP);
}
if (copy.hasParameter(PERSIST_MESSAGES)) {
pSession->setPersistMessages(copy.getParameter(PERSIST_MESSAGES) != "0");
copy.removeParameter(PERSIST_MESSAGES);
}
if (copy.hasParameter(SEND_NEXT_EXPECTED_MSG_SEQ_NUM)) {
pSession->setSendNextExpectedMsgSeqNum(copy.getParameter(SEND_NEXT_EXPECTED_MSG_SEQ_NUM) != "0");
copy.removeParameter(SEND_NEXT_EXPECTED_MSG_SEQ_NUM);
}
if (url != copy.toString()) {
h << "<META http-equiv='refresh' content=0;URL='" << copy.toString() << "'>";
}
TABLE table(b);
table.border(1).cellspacing(2).width(100).text();
{
CAPTION caption(b);
caption.text();
EM em(b);
em.text();
b << sessionID;
{
HR hr(b);
hr.text();
}
{
b << NBSP;
A a(b);
a.href("/resetSession" + copy.getParameterString()).text("RESET");
}
{
b << NBSP;
A a(b);
a.href("/refreshSession" + copy.getParameterString()).text("REFRESH");
}
}
showRow(b, "Enabled", pSession->isEnabled(), url);
showRow(b, "ConnectionType", std::string(pSession->isInitiator() ? "initiator" : "acceptor"));
showRow(b, "SessionTime", pSession->isSessionTime(UtcTimeStamp::now()));
showRow(b, "Logged On", pSession->isLoggedOn());
showRow(b, "Next Incoming", (int)pSession->getExpectedTargetNum(), url);
showRow(b, "Next Outgoing", (int)pSession->getExpectedSenderNum(), url);
showRow(b, SEND_REDUNDANT_RESENDREQUESTS, pSession->getSendRedundantResendRequests(), url);
showRow(b, CHECK_COMPID, pSession->getCheckCompId(), url);
showRow(b, CHECK_LATENCY, pSession->getCheckLatency(), url);
showRow(b, MAX_LATENCY, pSession->getMaxLatency(), url);
showRow(b, LOGON_TIMEOUT, pSession->getLogonTimeout(), url);
showRow(b, LOGOUT_TIMEOUT, pSession->getLogoutTimeout(), url);
showRow(b, RESET_ON_LOGON, pSession->getResetOnLogon(), url);
showRow(b, RESET_ON_LOGOUT, pSession->getResetOnLogout(), url);
showRow(b, RESET_ON_DISCONNECT, pSession->getResetOnDisconnect(), url);
showRow(b, REFRESH_ON_LOGON, pSession->getRefreshOnLogon(), url);
showRow(b, MILLISECONDS_IN_TIMESTAMP, pSession->getMillisecondsInTimeStamp(), url);
showRow(b, PERSIST_MESSAGES, pSession->getPersistMessages(), url);
showRow(b, SEND_NEXT_EXPECTED_MSG_SEQ_NUM, pSession->getSendNextExpectedMsgSeqNum(), url);
} catch (std::exception &e) {
b << e.what();
}
}
void HttpConnection::processResetSession(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
try {
HttpMessage copy = request;
std::string beginString = request.getParameter("BeginString");
std::string senderCompID = request.getParameter("SenderCompID");
std::string targetCompID = request.getParameter("TargetCompID");
std::string sessionQualifier;
if (copy.hasParameter("SessionQualifier")) {
sessionQualifier = copy.getParameter("SessionQualifier");
}
SessionID sessionID(beginString, senderCompID, targetCompID, sessionQualifier);
Session *pSession = Session::lookupSession(sessionID);
if (pSession == 0) {
throw SessionNotFound();
}
std::string sessionUrl = "/session" + request.getParameterString();
bool confirm = false;
if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
confirm = true;
pSession->reset();
copy.removeParameter("confirm");
}
if (confirm) {
h << "<META http-equiv='refresh' content=2;URL='" << "/session" << copy.getParameterString() << "'>";
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
{
A a(b);
a.href("/session" + copy.getParameterString()).text(sessionID.toString());
}
b << " has been reset";
} else {
{
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
b << "Are you sure you want to reset session ";
{
A a(b);
a.href(sessionUrl + request.getParameterString()).text(sessionID.toString());
}
b << "?";
}
{
CENTER center(b);
center.text();
b << "[";
{
A a(b);
a.href(request.toString() + "&confirm=1").text("YES, reset session");
}
b << "]" << NBSP << "[";
{
A a(b);
a.href(sessionUrl).text("NO, do not reset session");
}
b << "]";
}
}
} catch (std::exception &e) {
b << e.what();
}
}
void HttpConnection::processRefreshSession(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
try {
HttpMessage copy = request;
std::string beginString = request.getParameter("BeginString");
std::string senderCompID = request.getParameter("SenderCompID");
std::string targetCompID = request.getParameter("TargetCompID");
std::string sessionQualifier;
if (copy.hasParameter("SessionQualifier")) {
sessionQualifier = copy.getParameter("SessionQualifier");
}
SessionID sessionID(beginString, senderCompID, targetCompID, sessionQualifier);
Session *pSession = Session::lookupSession(sessionID);
if (pSession == 0) {
throw SessionNotFound();
}
std::string sessionUrl = "/session" + request.getParameterString();
bool confirm = false;
if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
confirm = true;
pSession->refresh();
copy.removeParameter("confirm");
}
if (confirm) {
h << "<META http-equiv='refresh' content=2;URL='" << "/session" << copy.getParameterString() << "'>";
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
{
A a(b);
a.href("/session" + copy.getParameterString()).text(sessionID.toString());
}
b << " has been refreshed";
} else {
{
CENTER center(b);
center.text();
H2 h2(b);
h2.text();
b << "Are you sure you want to refresh session ";
{
A a(b);
a.href(sessionUrl + request.getParameterString()).text(sessionID.toString());
}
b << "?";
}
{
CENTER center(b);
center.text();
b << "[";
{
A a(b);
a.href(request.toString() + "&confirm=1").text("YES, refresh session");
}
b << "]" << NBSP << "[";
{
A a(b);
a.href(sessionUrl).text("NO, do not refresh session");
}
b << "]";
}
}
} catch (std::exception &e) {
b << e.what();
}
}
void HttpConnection::showRow(std::stringstream &s, const std::string &name, bool value, const std::string &url) {
{
TR tr(s);
tr.text();
{
TD td(s);
td.text(name);
}
{
TD td(s);
td.text(value ? "yes" : "no");
}
{
TD td(s);
td.text();
CENTER center(s);
center.text();
if (url.size()) {
std::stringstream href;
href << url << "&" << name << "=" << !value;
A a(s);
a.href(href.str()).text("toggle");
}
}
}
}
void HttpConnection::showRow(
std::stringstream &s,
const std::string &name,
const std::string &value,
const std::string &url) {
{
TR tr(s);
tr.text();
{
TD td(s);
td.text(name);
}
{
TD td(s);
td.text(value);
}
{
TD td(s);
td.text();
CENTER center(s);
center.text();
}
}
}
void HttpConnection::showRow(std::stringstream &s, const std::string &name, int value, const std::string &url) {
{
TR tr(s);
tr.text();
{
TD td(s);
td.text(name);
}
{
TD td(s);
td.text(value);
}
{
TD td(s);
td.text();
CENTER center(s);
center.text();
{
std::stringstream href;
href << url << "&" << name << "=" << value - 10;
A a(s);
a.href(href.str()).text("<<");
}
s << NBSP;
{
std::stringstream href;
href << url << "&" << name << "=" << value - 1;
A a(s);
a.href(href.str()).text("<");
}
s << NBSP << "|" << NBSP;
{
std::stringstream href;
href << url << "&" << name << "=" << value + 1;
A a(s);
a.href(href.str()).text(">");
}
s << NBSP;
{
std::stringstream href;
href << url << "&" << name << "=" << value + 10;
A a(s);
a.href(href.str()).text(">>");
}
}
}
}
}