From 34efc9a5ae429e3a94fb8e92cecc289e40ba7bf1 Mon Sep 17 00:00:00 2001 From: john30 Date: Fri, 1 May 2015 08:34:27 +0200 Subject: [PATCH] introduced optional HTTP port --- src/ebusd/network.cpp | 98 ++++++++++++++++++----------- src/ebusd/network.h | 139 +++++++++++++++++++++++++++++------------- 2 files changed, 160 insertions(+), 77 deletions(-) diff --git a/src/ebusd/network.cpp b/src/ebusd/network.cpp index ed285828..dbbcbcae 100644 --- a/src/ebusd/network.cpp +++ b/src/ebusd/network.cpp @@ -71,8 +71,8 @@ void Connection::run() #endif #endif - time_t listenSince = 0; bool closed = false; + NetMessage message(m_isHttp); while (!closed) { #ifdef HAVE_PPOLL @@ -109,37 +109,39 @@ void Connection::run() #endif } - if (newData || m_listening) { + if (newData || message.isListening()) { char data[256]; - size_t datalen = 0; - - if (newData) { - if (!m_socket->isValid()) - break; - - datalen = m_socket->recv(data, sizeof(data)-1); - - // remove closed socket - if (datalen <= 0) - break; - } - - // decode client data - data[datalen] = '\0'; - NetMessage message(data, m_listening, listenSince); - m_netQueue->add(&message); - - // wait for result - logDebug(lf_network, "[%05d] wait for result", getID()); - string result = message.getResult(); if (!m_socket->isValid()) break; - m_socket->send(result.c_str(), result.size()); - m_listening = message.isListening(listenSince); + if (newData) { + size_t datalen = m_socket->recv(data, sizeof(data)-1); - if (message.isDisconnect()) + // remove closed socket + if (datalen <= 0) + break; + + data[datalen] = '\0'; + } else { + data[0] = '\0'; + } + + // decode client data + if (message.add(data)) { + m_netQueue->add(&message); + + // wait for result + logDebug(lf_network, "[%05d] wait for result", getID()); + string result = message.getResult(); + + if (!m_socket->isValid()) + break; + + m_socket->send(result.c_str(), result.size()); + } + + if (message.isDisconnect() || !m_socket->isValid()) break; } @@ -151,7 +153,7 @@ void Connection::run() } -Network::Network(const bool local, const uint16_t port, WQueue* netQueue) +Network::Network(const bool local, const uint16_t port, const uint16_t httpPort, WQueue* netQueue) : m_netQueue(netQueue), m_listening(false) { if (local) @@ -162,6 +164,11 @@ Network::Network(const bool local, const uint16_t port, WQueue* net if (m_tcpServer != NULL && m_tcpServer->start() == 0) m_listening = true; + if (httpPort>0) { + m_httpServer = new TCPServer(httpPort, "0.0.0.0"); + m_httpServer->start(); + } else + m_httpServer = NULL; } Network::~Network() @@ -192,9 +199,9 @@ void Network::run() // set timeout tdiff.tv_sec = 1; tdiff.tv_nsec = 0; - + int socketCount = m_httpServer ? 2 : 1; #ifdef HAVE_PPOLL - int nfds = 2; + int nfds = 1+socketCount; struct pollfd fds[nfds]; memset(fds, 0, sizeof(fds)); @@ -204,6 +211,11 @@ void Network::run() fds[1].fd = m_tcpServer->getFD(); fds[1].events = POLLIN; + + if (m_httpServer) { + fds[2].fd = m_httpServer->getFD(); + fds[2].events = POLLIN; + } #else #ifdef HAVE_PSELECT int maxfd; @@ -212,9 +224,15 @@ void Network::run() FD_ZERO(&checkfds); FD_SET(m_notify.notifyFD(), &checkfds); FD_SET(m_tcpServer->getFD(), &checkfds); + if (m_httpServer) { + FD_SET(m_httpServer->getFD(), &checkfds); + } - (m_notify.notifyFD() > m_tcpServer->getFD()) ? - (maxfd = m_notify.notifyFD()) : (maxfd = m_tcpServer->getFD()); + maxfd = (m_notify.notifyFD() > m_tcpServer->getFD()) ? + m_notify.notifyFD() : m_tcpServer->getFD(); + if (m_httpServer && m_httpServer->getFD()>maxfd) { + maxfd = m_httpServer->getFD(); + } #endif #endif @@ -236,7 +254,7 @@ void Network::run() cleanConnections(); continue; } - + bool newData = false, isHttp = false; #ifdef HAVE_PPOLL // new data from notify if (fds[0].revents & POLLIN) { @@ -245,6 +263,10 @@ void Network::run() // new data from socket if (fds[1].revents & POLLIN) { + newData = true; + } else if (m_httpServer && fds[2].revents & POLLIN) { + newData = isHttp = true; + } #else #ifdef HAVE_PSELECT // new data from notify @@ -254,20 +276,24 @@ void Network::run() // new data from socket if (FD_ISSET(m_tcpServer->getFD(), &readfds)) { + newData = true; + } else if (m_httpServer && FD_ISSET(m_httpServer->getFD(), &readfds)) { + newData = isHttp = true; + } #endif #endif - - TCPSocket* socket = m_tcpServer->newSocket(); + if (newData) { + TCPSocket* socket = (isHttp ? m_httpServer : m_tcpServer)->newSocket(); if (socket == NULL) continue; - Connection* connection = new Connection(socket, m_netQueue); + Connection* connection = new Connection(socket, isHttp, m_netQueue); if (connection == NULL) continue; connection->start("connection"); m_connections.push_back(connection); - logInfo(lf_network, "[%05d] connection opened %s", connection->getID(), socket->getIP().c_str()); + logInfo(lf_network, "[%05d] %s connection opened %s", connection->getID(), isHttp ? "HTTP" : "client", socket->getIP().c_str()); } } } diff --git a/src/ebusd/network.h b/src/ebusd/network.h index 50916b95..fdbb2d12 100644 --- a/src/ebusd/network.h +++ b/src/ebusd/network.h @@ -26,36 +26,36 @@ #include "notify.h" #include "thread.h" #include +#include +#include /** \file network.h */ using namespace std; -/** forward declaration for class connection */ +/** Forward declaration for @a Connection. */ class Connection; /** - * class for data/message transfer between connection and baseloop. + * Class for data/message transfer between @a Connection and @a MainLoop. */ class NetMessage { public: /** - * constructs a new instance with data received from the client. - * @param data from client. - * @param listening whether the client is in listening mode. - * @param listenSince start timestamp of listening update. + * Constructor. + * @param isHttp whether this is a HTTP message. */ - NetMessage(const string data, const bool listening, const time_t listenSince) - : m_data(data), m_resultSet(false), m_disconnect(false), m_listening(listening), m_listenSince(listenSince) + NetMessage(const bool isHttp) + : m_isHttp(isHttp), m_resultSet(false), m_disconnect(false), m_listening(false), m_listenSince(0) { pthread_mutex_init(&m_mutex, NULL); pthread_cond_init(&m_cond, NULL); } /** - * destructor. + * Destructor. */ ~NetMessage() { @@ -73,10 +73,55 @@ private: public: /** - * get the data string. - * @return the data string. + * Add request data received from the client. + * @param request the request data from the client. + * @param listening whether the client is in listening mode. + * @param listenSince start timestamp of listening update. + * @return true when the request is complete and the response shall be prepared. */ - string getData() const { return m_data; } + bool add(string request) + { + if (request.length()>0) { + request.erase(remove(request.begin(), request.end(), '\r'), request.end()); + m_request.append(request); + } + size_t pos = m_request.find(m_isHttp ? "\n\n" : "\n"); + if (pos!=string::npos) { + if (m_isHttp) { + pos = m_request.find("\n"); + m_request.resize(pos); // reduce to first line + // typical first line: GET /ehp/outsidetemp HTTP/1.1 + pos = m_request.rfind(" HTTP/"); + if (pos!=string::npos) { + m_request.resize(pos); // remove "HTTP/x.x" suffix + } + pos = 0; + while ((pos=m_request.find('%', pos))!=string::npos && pos+2<=m_request.length()) { + unsigned int value1, value2; + if (sscanf("%1x%1x", m_request.c_str()+pos+1, &value1, &value2)<2) + break; + m_request[pos] = (char)(((value1&0x0f)<<4)|(value2&0x0f)); + m_request.erase(pos+1, 2); + } + } else if (pos+1==m_request.length()) { + m_request.resize(pos); // reduce to complete lines + } + return true; + } + return m_request.length()==0 && m_listening; + } + + /** + * Return whether this is a HTTP message. + * @return whether this is a HTTP message. + */ + bool isHttp() const { return m_isHttp; } + + /** + * Return the request string. + * @return the request string. + */ + string getRequest() const { return m_request; } /** * Wait for the result being set and return the result string. @@ -89,9 +134,13 @@ public: while (!m_resultSet) pthread_cond_wait(&m_cond, &m_mutex); + m_request.clear(); + string result = m_result; + m_result.clear(); + m_resultSet = false; pthread_mutex_unlock(&m_mutex); - return m_result; + return result; } /** @@ -103,12 +152,12 @@ public: */ void setResult(const string result, const bool listening, const time_t listenUntil, const bool disconnect) { + pthread_mutex_lock(&m_mutex); m_result = result; m_disconnect = disconnect; m_listening = listening; m_listenSince = listenUntil; m_resultSet = true; - pthread_mutex_lock(&m_mutex); pthread_cond_signal(&m_cond); pthread_mutex_unlock(&m_mutex); } @@ -118,7 +167,7 @@ public: * @param listenSince set to the start time from which to add updates (inclusive). * @return whether the client is in listening mode. */ - bool isListening(time_t& listenSince) { listenSince = m_listenSince; return m_listening; } + bool isListening(time_t* listenSince=NULL) { if (listenSince) *listenSince = m_listenSince; return m_listening; } /** * Return whether the client shall be disconnected. @@ -127,28 +176,31 @@ public: bool isDisconnect() { return m_disconnect; } private: - /** the data string */ - string m_data; + /** whether this is a HTTP message. */ + const bool m_isHttp; + + /** the request string. */ + string m_request; /** whether the result was already set. */ bool m_resultSet; - /** the result string */ + /** the result string. */ string m_result; /** set to true when the client shall be disconnected. */ bool m_disconnect; - /** mutex variable for exclusive lock */ + /** mutex variable for exclusive lock. */ pthread_mutex_t m_mutex; - /** condition variable for exclusive lock */ + /** condition variable for exclusive lock. */ pthread_cond_t m_cond; - /** whether the client is in listening mode */ + /** whether the client is in listening mode. */ bool m_listening; - /** start timestamp of listening update */ + /** start timestamp of listening update. */ time_t m_listenSince; }; @@ -161,12 +213,13 @@ class Connection : public Thread public: /** - * create a new connection instance. - * @param socket the tcp socket for communication. - * @param netQueue the remote queue for network messages. + * Constructor. + * @param socket the @a TCPSocket for communication. + * @param isHttp whether this is a HTTP message. + * @param netQueue the remote @a WQueue for handling @a NetMessage instances. */ - Connection(TCPSocket* socket, WQueue* netQueue) - : m_socket(socket), m_netQueue(netQueue), m_listening(false) + Connection(TCPSocket* socket, const bool isHttp, WQueue* netQueue) + : m_isHttp(isHttp), m_socket(socket), m_netQueue(netQueue) { m_id = ++m_ids; } virtual ~Connection() { if (m_socket) delete m_socket; } @@ -176,35 +229,35 @@ public: virtual void run(); /** - * close active connection. + * Stop this connection. */ virtual void stop() { m_notify.notify(); Thread::stop(); } /** - * return own connection id. - * @return id of current connection. + * Return the ID of this connection. + * @return the ID of this connection. */ int getID() { return m_id; } private: - /** the tcp socket instance */ + /** whether this is a HTTP connection. */ + const bool m_isHttp; + + /** the @a TCPSocket for communication. */ TCPSocket* m_socket; - /** remote queue for network messages */ + /** the remote @a WQueue for handling @a NetMessage instances. */ WQueue* m_netQueue; - /** notification object for shutdown procedure */ + /** notification object for shutdown procedure. */ Notify m_notify; - /** id of current connection */ + /** the ID of this connection. */ int m_id; - /** sumary for opened connections */ + /** the IF of the last opened connection. */ static int m_ids; - /** whether the client is in listening mode */ - bool m_listening; - }; /** @@ -217,10 +270,11 @@ public: /** * create a network instance and listening for incoming connections. * @param local true to accept connections only for local host. - * @param port the tcp port to listening. + * @param port the port to listen for command line connections. + * @param httpPort the port to listen for HTTP connections, or 0. * @param netQueue the remote queue for network messages. */ - Network(const bool local, const uint16_t port, WQueue* netQueue); + Network(const bool local, const uint16_t port, const uint16_t httpPort, WQueue* netQueue); /** * destructor. @@ -244,9 +298,12 @@ private: /** the @a MainLoop queue for transferring @a NetMessage instances. */ WQueue* m_netQueue; - /** the @a TCPServer instance. */ + /** the command line @a TCPServer instance. */ TCPServer* m_tcpServer; + /** the HTTP @a TCPServer instance, or NULL. */ + TCPServer* m_httpServer; + /** @a Notify object for shutdown procedure. */ Notify m_notify;