extracted http client
This commit is contained in:
Regular → Executable
+15
-66
@@ -26,6 +26,7 @@
|
||||
#include <algorithm>
|
||||
#include "ebusd/main.h"
|
||||
#include "lib/utils/log.h"
|
||||
#include "lib/utils/httpclient.h"
|
||||
#include "lib/ebus/data.h"
|
||||
|
||||
namespace ebusd {
|
||||
@@ -301,13 +302,12 @@ void MainLoop::run() {
|
||||
}
|
||||
}
|
||||
if (m_runUpdateCheck && !m_shutdown && now > nextCheckRun) {
|
||||
TCPClient client;
|
||||
TCPSocket* socket = client.connect("ebusd.eu", 80);
|
||||
if (socket) {
|
||||
socket->setTimeout(5);
|
||||
HttpClient client;
|
||||
if (!client.connect("ebusd.eu", 80, PACKAGE_NAME "/" PACKAGE_VERSION)) {
|
||||
logError(lf_main, "update check connect error");
|
||||
} else {
|
||||
ostringstream ostr;
|
||||
ostr << "{\"v\":\"" << PACKAGE_VERSION "\""
|
||||
<< ",\"r\":\"" << REVISION << "\""
|
||||
ostr << "{\"v\":\"" PACKAGE_VERSION "\",\"r\":\"" REVISION << "\""
|
||||
#if defined(__amd64__) || defined(__x86_64__) || defined(__ia64__) || defined(__IA64__)
|
||||
<< ",\"a\":\"amd64\""
|
||||
#elif defined(__aarch64__)
|
||||
@@ -327,69 +327,18 @@ void MainLoop::run() {
|
||||
}
|
||||
m_busHandler->formatUpdateInfo(&ostr);
|
||||
ostr << "}";
|
||||
string str = ostr.str();
|
||||
ostr.clear();
|
||||
ostr.str("");
|
||||
ostr << "POST /updatecheck/ HTTP/1.0\r\n"
|
||||
<< "Host: ebusd.eu" << "\r\n"
|
||||
<< "User-Agent: " << PACKAGE_NAME << "/" << PACKAGE_VERSION << "\r\n"
|
||||
<< "Content-Type: application/json; charset=utf-8\r\n"
|
||||
<< "Content-Length: " << dec << str.length() << "\r\n"
|
||||
<< "\r\n"
|
||||
<< 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);
|
||||
string response;
|
||||
if (!client.post("/updatecheck/", ostr.str(), response)) {
|
||||
logError(lf_main, "update check error: %s", response.c_str());
|
||||
} else {
|
||||
m_updateCheck = response.empty() ? "unknown" : response;
|
||||
logNotice(lf_main, "update check: %s", response.c_str());
|
||||
if (!dataSinks.empty()) {
|
||||
for (const auto dataSink : dataSinks) {
|
||||
dataSink->notifyUpdateCheckResult(response == "OK" ? "" : m_updateCheck);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
Regular → Executable
+6
-11
@@ -1,18 +1,13 @@
|
||||
add_definitions(-Wconversion)
|
||||
|
||||
set(libutils_a_SOURCES
|
||||
log.cpp
|
||||
log.h
|
||||
tcpsocket.cpp
|
||||
tcpsocket.h
|
||||
thread.cpp
|
||||
thread.h
|
||||
clock.cpp
|
||||
clock.h
|
||||
log.h log.cpp
|
||||
tcpsocket.h tcpsocket.cpp
|
||||
thread.h thread.cpp
|
||||
clock.h clock.cpp
|
||||
queue.h
|
||||
notify.h
|
||||
rotatefile.cpp
|
||||
rotatefile.h
|
||||
)
|
||||
rotatefile.h rotatefile.cpp
|
||||
httpclient.h httpclient.cpp)
|
||||
|
||||
add_library(utils ${libutils_a_SOURCES})
|
||||
|
||||
Regular → Executable
+6
-10
@@ -4,18 +4,14 @@ AM_CXXFLAGS = -I$(top_srcdir)/src \
|
||||
|
||||
noinst_LIBRARIES = libutils.a
|
||||
|
||||
libutils_a_SOURCES = log.cpp \
|
||||
log.h \
|
||||
tcpsocket.cpp \
|
||||
tcpsocket.h \
|
||||
thread.cpp \
|
||||
thread.h \
|
||||
clock.cpp \
|
||||
clock.h \
|
||||
libutils_a_SOURCES = log.h log.cpp \
|
||||
tcpsocket.h tcpsocket.cpp \
|
||||
thread.h thread.cpp \
|
||||
clock.h clock.cpp \
|
||||
queue.h \
|
||||
notify.h \
|
||||
rotatefile.cpp \
|
||||
rotatefile.h
|
||||
rotatefile.h rotatefile.cpp \
|
||||
httpclient.h httpclient.cpp
|
||||
|
||||
distclean-local:
|
||||
-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