extracted http client
This commit is contained in:
Regular → Executable
+15
-66
@@ -26,6 +26,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "ebusd/main.h"
|
#include "ebusd/main.h"
|
||||||
#include "lib/utils/log.h"
|
#include "lib/utils/log.h"
|
||||||
|
#include "lib/utils/httpclient.h"
|
||||||
#include "lib/ebus/data.h"
|
#include "lib/ebus/data.h"
|
||||||
|
|
||||||
namespace ebusd {
|
namespace ebusd {
|
||||||
@@ -301,13 +302,12 @@ void MainLoop::run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_runUpdateCheck && !m_shutdown && now > nextCheckRun) {
|
if (m_runUpdateCheck && !m_shutdown && now > nextCheckRun) {
|
||||||
TCPClient client;
|
HttpClient client;
|
||||||
TCPSocket* socket = client.connect("ebusd.eu", 80);
|
if (!client.connect("ebusd.eu", 80, PACKAGE_NAME "/" PACKAGE_VERSION)) {
|
||||||
if (socket) {
|
logError(lf_main, "update check connect error");
|
||||||
socket->setTimeout(5);
|
} else {
|
||||||
ostringstream ostr;
|
ostringstream ostr;
|
||||||
ostr << "{\"v\":\"" << PACKAGE_VERSION "\""
|
ostr << "{\"v\":\"" PACKAGE_VERSION "\",\"r\":\"" REVISION << "\""
|
||||||
<< ",\"r\":\"" << REVISION << "\""
|
|
||||||
#if defined(__amd64__) || defined(__x86_64__) || defined(__ia64__) || defined(__IA64__)
|
#if defined(__amd64__) || defined(__x86_64__) || defined(__ia64__) || defined(__IA64__)
|
||||||
<< ",\"a\":\"amd64\""
|
<< ",\"a\":\"amd64\""
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
@@ -327,69 +327,18 @@ void MainLoop::run() {
|
|||||||
}
|
}
|
||||||
m_busHandler->formatUpdateInfo(&ostr);
|
m_busHandler->formatUpdateInfo(&ostr);
|
||||||
ostr << "}";
|
ostr << "}";
|
||||||
string str = ostr.str();
|
string response;
|
||||||
ostr.clear();
|
if (!client.post("/updatecheck/", ostr.str(), response)) {
|
||||||
ostr.str("");
|
logError(lf_main, "update check error: %s", response.c_str());
|
||||||
ostr << "POST /updatecheck/ HTTP/1.0\r\n"
|
} else {
|
||||||
<< "Host: ebusd.eu" << "\r\n"
|
m_updateCheck = response.empty() ? "unknown" : response;
|
||||||
<< "User-Agent: " << PACKAGE_NAME << "/" << PACKAGE_VERSION << "\r\n"
|
logNotice(lf_main, "update check: %s", response.c_str());
|
||||||
<< "Content-Type: application/json; charset=utf-8\r\n"
|
if (!dataSinks.empty()) {
|
||||||
<< "Content-Length: " << dec << str.length() << "\r\n"
|
for (const auto dataSink : dataSinks) {
|
||||||
<< "\r\n"
|
dataSink->notifyUpdateCheckResult(response == "OK" ? "" : m_updateCheck);
|
||||||
<< str;
|
|
||||||
str = ostr.str();
|
|
||||||
const char* cstr = str.c_str();
|
|
||||||
size_t len = str.size();
|
|
||||||
for (size_t pos = 0; pos < len; ) {
|
|
||||||
ssize_t sent = socket->send(cstr + pos, len - pos);
|
|
||||||
if (sent < 0) {
|
|
||||||
len = 0;
|
|
||||||
logError(lf_main, "update check send error");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pos += sent;
|
|
||||||
}
|
|
||||||
if (len) {
|
|
||||||
char buf[512];
|
|
||||||
ssize_t received = socket->recv(buf, sizeof(buf));
|
|
||||||
string result;
|
|
||||||
if (received > 15) { // "HTTP/1.1 200 OK"
|
|
||||||
buf[received - (received == sizeof(buf) ? 1 : 0)] = 0;
|
|
||||||
result = string(buf);
|
|
||||||
}
|
|
||||||
if (result.substr(0, 5) == "HTTP/") {
|
|
||||||
string message;
|
|
||||||
size_t pos = result.find("\r\n\r\n");
|
|
||||||
if (pos != string::npos) {
|
|
||||||
message = result.substr(pos+4);
|
|
||||||
}
|
}
|
||||||
pos = result.find(" ");
|
|
||||||
if (pos != string::npos) {
|
|
||||||
result = result.substr(pos+1);
|
|
||||||
}
|
|
||||||
pos = result.find("\r\n");
|
|
||||||
if (pos != string::npos) {
|
|
||||||
result = result.substr(0, pos);
|
|
||||||
}
|
|
||||||
if (result == "200 OK") {
|
|
||||||
m_updateCheck = message == "" ? "unknown" : message;
|
|
||||||
logNotice(lf_main, "update check: %s", message.c_str());
|
|
||||||
if (!dataSinks.empty()) {
|
|
||||||
for (const auto dataSink : dataSinks) {
|
|
||||||
dataSink->notifyUpdateCheckResult(message == "OK" ? "" : m_updateCheck);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logError(lf_main, "update check error: %s", result.c_str());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logError(lf_main, "update check receive error");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete socket;
|
|
||||||
socket = NULL;
|
|
||||||
} else {
|
|
||||||
logError(lf_main, "update check connect error");
|
|
||||||
}
|
}
|
||||||
nextCheckRun = now + CHECK_DELAY;
|
nextCheckRun = now + CHECK_DELAY;
|
||||||
}
|
}
|
||||||
|
|||||||
Regular → Executable
+6
-11
@@ -1,18 +1,13 @@
|
|||||||
add_definitions(-Wconversion)
|
add_definitions(-Wconversion)
|
||||||
|
|
||||||
set(libutils_a_SOURCES
|
set(libutils_a_SOURCES
|
||||||
log.cpp
|
log.h log.cpp
|
||||||
log.h
|
tcpsocket.h tcpsocket.cpp
|
||||||
tcpsocket.cpp
|
thread.h thread.cpp
|
||||||
tcpsocket.h
|
clock.h clock.cpp
|
||||||
thread.cpp
|
|
||||||
thread.h
|
|
||||||
clock.cpp
|
|
||||||
clock.h
|
|
||||||
queue.h
|
queue.h
|
||||||
notify.h
|
notify.h
|
||||||
rotatefile.cpp
|
rotatefile.h rotatefile.cpp
|
||||||
rotatefile.h
|
httpclient.h httpclient.cpp)
|
||||||
)
|
|
||||||
|
|
||||||
add_library(utils ${libutils_a_SOURCES})
|
add_library(utils ${libutils_a_SOURCES})
|
||||||
|
|||||||
Regular → Executable
+6
-10
@@ -4,18 +4,14 @@ AM_CXXFLAGS = -I$(top_srcdir)/src \
|
|||||||
|
|
||||||
noinst_LIBRARIES = libutils.a
|
noinst_LIBRARIES = libutils.a
|
||||||
|
|
||||||
libutils_a_SOURCES = log.cpp \
|
libutils_a_SOURCES = log.h log.cpp \
|
||||||
log.h \
|
tcpsocket.h tcpsocket.cpp \
|
||||||
tcpsocket.cpp \
|
thread.h thread.cpp \
|
||||||
tcpsocket.h \
|
clock.h clock.cpp \
|
||||||
thread.cpp \
|
|
||||||
thread.h \
|
|
||||||
clock.cpp \
|
|
||||||
clock.h \
|
|
||||||
queue.h \
|
queue.h \
|
||||||
notify.h \
|
notify.h \
|
||||||
rotatefile.cpp \
|
rotatefile.h rotatefile.cpp \
|
||||||
rotatefile.h
|
httpclient.h httpclient.cpp
|
||||||
|
|
||||||
distclean-local:
|
distclean-local:
|
||||||
-rm -f Makefile.in
|
-rm -f Makefile.in
|
||||||
|
|||||||
Executable
+132
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* ebusd - daemon for communication with eBUS heating systems.
|
||||||
|
* Copyright (C) 2018 John Baier <ebusd@ebusd.eu>
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lib/utils/httpclient.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <sstream>
|
||||||
|
#include <ios>
|
||||||
|
|
||||||
|
namespace ebusd {
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::ostringstream;
|
||||||
|
using std::dec;
|
||||||
|
using std::hex;
|
||||||
|
|
||||||
|
bool HttpClient::connect(const string& host, const uint16_t port, const string& userAgent, const int timeout) {
|
||||||
|
if (m_socket) {
|
||||||
|
delete m_socket;
|
||||||
|
m_socket = nullptr;
|
||||||
|
}
|
||||||
|
TCPSocket* socket = m_client.connect(host, port);
|
||||||
|
if (!socket) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
socket->setTimeout(timeout);
|
||||||
|
m_socket = socket;
|
||||||
|
m_host = host;
|
||||||
|
m_userAgent = userAgent;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpClient::get(const string& uri, const string& body, string& response) {
|
||||||
|
return request("GET", uri, body, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpClient::post(const string& uri, const string& body, string& response) {
|
||||||
|
return request("POST", uri, body, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpClient::request(const string& method, const string& uri, const string& body, string& response) {
|
||||||
|
if (!m_socket) {
|
||||||
|
response = "not connected";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ostringstream ostr;
|
||||||
|
ostr << method << " " << uri << " HTTP/1.0\r\n"
|
||||||
|
<< "Host: " << m_host << "\r\n";
|
||||||
|
if (!m_userAgent.empty()) {
|
||||||
|
ostr << "User-Agent: " << m_userAgent << "\r\n";
|
||||||
|
}
|
||||||
|
if (body.empty()) {
|
||||||
|
ostr << "\r\n";
|
||||||
|
} else {
|
||||||
|
ostr << "Content-Type: application/json; charset=utf-8\r\n"
|
||||||
|
<< "Content-Length: " << dec << body.length() << "\r\n"
|
||||||
|
<< "\r\n"
|
||||||
|
<< body;
|
||||||
|
}
|
||||||
|
string str = ostr.str();
|
||||||
|
size_t len = str.size();
|
||||||
|
const char* cstr = str.c_str();
|
||||||
|
for (size_t pos = 0; pos < len; ) {
|
||||||
|
ssize_t sent = m_socket->send(cstr + pos, len - pos);
|
||||||
|
if (sent < 0) {
|
||||||
|
response = "send error";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pos += sent;
|
||||||
|
}
|
||||||
|
if (!m_buffer) {
|
||||||
|
m_buffer = (char *) malloc(1024);
|
||||||
|
if (!m_buffer) {
|
||||||
|
response = "memory allocation";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_bufferSize = 1024;
|
||||||
|
}
|
||||||
|
ssize_t received = m_socket->recv(m_buffer, m_bufferSize);
|
||||||
|
if (received <= 0) {
|
||||||
|
response = "receive error";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
string result = string(m_buffer, 0, static_cast<unsigned>(received)); // expect "HTTP/1.1 200 OK"
|
||||||
|
size_t pos = result.find(' ');
|
||||||
|
if (result.substr(0, 5) != "HTTP/" || pos == string::npos || pos > 8) {
|
||||||
|
response = "receive error (headers)";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (result.substr(pos+1, 6) != "200 OK") {
|
||||||
|
size_t endpos = result.find("\r\n", pos+1);
|
||||||
|
response = "receive error: " + result.substr(pos+1, endpos == string::npos ? endpos : endpos-pos-1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pos = result.find("\r\n\r\n");
|
||||||
|
while (pos == string::npos && result.length() < 256*1024) {
|
||||||
|
received = m_socket->recv(m_buffer, m_bufferSize);
|
||||||
|
if (received < 0) {
|
||||||
|
response = "receive error";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (received == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
size_t oldLength = result.length();
|
||||||
|
result += string(m_buffer, 0, static_cast<unsigned>(received));
|
||||||
|
pos = result.find("\r\n\r\n", oldLength - 3);
|
||||||
|
}
|
||||||
|
if (pos == string::npos) {
|
||||||
|
response = "receive error (headers)";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
response = result.substr(pos+4);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ebusd
|
||||||
Executable
+111
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* ebusd - daemon for communication with eBUS heating systems.
|
||||||
|
* Copyright (C) 2018 John Baier <ebusd@ebusd.eu>
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIB_UTILS_HTTP_H_
|
||||||
|
#define LIB_UTILS_HTTP_H_
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include "lib/utils/tcpsocket.h"
|
||||||
|
|
||||||
|
|
||||||
|
/** typedef for referencing @a sockaddr_in within namespace. */
|
||||||
|
typedef struct sockaddr_in socketaddress;
|
||||||
|
|
||||||
|
namespace ebusd {
|
||||||
|
|
||||||
|
/** \file lib/utils/http.h */
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::ifstream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for handling HTTP client requests.
|
||||||
|
*/
|
||||||
|
class HttpClient {
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
HttpClient() : m_socket(nullptr), m_bufferSize(0), m_buffer(nullptr) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~HttpClient() {
|
||||||
|
if (m_socket) {
|
||||||
|
delete m_socket;
|
||||||
|
m_socket = nullptr;
|
||||||
|
}
|
||||||
|
if (m_buffer) {
|
||||||
|
free(m_buffer);
|
||||||
|
m_buffer = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to the specified server.
|
||||||
|
* @param host the host name to connect to.
|
||||||
|
* @param port the port to connect to.
|
||||||
|
* @param timeout the timeout in seconds, defaults to 5 seconds.
|
||||||
|
* @param userAgent the optional user agent to send in the request header.
|
||||||
|
* @return true on success, false on connect failure.
|
||||||
|
*/
|
||||||
|
bool connect(const string& host, uint16_t port, const string& userAgent = "", int timeout = 5);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a GET request.
|
||||||
|
* @param uri the URI string.
|
||||||
|
* @param body the optional body to send.
|
||||||
|
* @param response the response body from the server (or the HTTP header on error).
|
||||||
|
* @return true on success, false on error.
|
||||||
|
*/
|
||||||
|
bool get(const string& uri, const string& body, string& response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a POST request.
|
||||||
|
* @param uri the URI string.
|
||||||
|
* @param body the optional body to send.
|
||||||
|
* @param response the response body from the server (or the HTTP header on error).
|
||||||
|
* @return true on success, false on error.
|
||||||
|
*/
|
||||||
|
bool post(const string& uri, const string& body, string& response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute an arbitrary request.
|
||||||
|
* @param uri the URI string.
|
||||||
|
* @param body the optional body to send.
|
||||||
|
* @param response the response body from the server (or the HTTP header on error).
|
||||||
|
* @return true on success, false on error.
|
||||||
|
*/
|
||||||
|
bool request(const string& method, const string& uri, const string& body, string& response);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TCPClient m_client;
|
||||||
|
string m_host;
|
||||||
|
string m_userAgent;
|
||||||
|
TCPSocket* m_socket;
|
||||||
|
size_t m_bufferSize;
|
||||||
|
char* m_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ebusd
|
||||||
|
|
||||||
|
#endif //LIB_UTILS_HTTP_H_
|
||||||
Reference in New Issue
Block a user