1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-16 00:00:02 +01:00
VILLASnode/lib/api/sessions/http.cpp

201 lines
4.3 KiB
C++

/** HTTP Api session.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* 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
* 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 <sstream>
#include <libwebsockets.h>
#include <villas/exceptions.hpp>
#include <villas/web.hpp>
#include <villas/log.hpp>
#include <villas/config.h>
#include <villas/api/sessions/http.hpp>
using namespace villas;
using namespace villas::node;
using namespace villas::node::api::sessions;
Http::Http(Api *a, lws *w) :
Wsi(a, w),
headersSent(false)
{
int hdrlen, options = -1, version;
char *uri;
if ((hdrlen = lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI)))
options = 1;
else if ((hdrlen = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)))
options = 0;
uri = new char[hdrlen + 1];
lws_hdr_copy(wsi, uri, hdrlen + 1, options ? WSI_TOKEN_OPTIONS_URI : WSI_TOKEN_POST_URI);
/* Parse request URI */
sscanf(uri, "/api/v%d", (int *) &version);
if (version != api::version)
throw RuntimeError("Unsupported API version: {}", version);
/* This is an OPTIONS request.
*
* We immediatly send headers and close the connection
* without waiting for a POST body */
if (options)
lws_callback_on_writable(wsi);
delete uri;
}
void Http::read(void *in, size_t len)
{
request.buffer.append((const char *) in, len);
}
int Http::complete()
{
json_t *req;
req = request.buffer.decode();
if (!req)
return 0;
request.buffer.clear();
request.queue.push(req);
return 1;
}
int Http::write()
{
int ret;
if (!headersSent) {
std::stringstream headers;
json_t *resp = response.queue.pop();
response.buffer.clear();
response.buffer.encode(resp);
json_decref(resp);
headers << "HTTP/1.1 200 OK\r\n"
<< "Content-type: application/json\r\n"
<< "User-agent: " USER_AGENT "\r\n"
<< "Connection: close\r\n"
<< "Content-Length: " << response.buffer.size() << "\r\n"
<< "Access-Control-Allow-Origin: *\r\n"
<< "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
<< "Access-Control-Allow-Headers: Content-Type\r\n"
<< "Access-Control-Max-Age: 86400\r\n"
<< "\r\n";
ret = lws_write(wsi, (unsigned char *) headers.str().data(), headers.str().size(), LWS_WRITE_HTTP_HEADERS);
if (ret < 0)
return -1;
/* No wait, until we can send the body */
headersSent = true;
lws_callback_on_writable(wsi);
return 0;
}
else {
ret = lws_write(wsi, (unsigned char *) response.buffer.data(), response.buffer.size(), LWS_WRITE_HTTP_FINAL);
if (ret < 0)
return -1;
headersSent = false;
return 1;
}
}
std::string Http::getName()
{
std::stringstream ss;
ss << Wsi::getName() << ", mode=http";
return ss.str();
}
int api_http_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
int ret;
lws_context *ctx = lws_get_context(wsi);
void *user_ctx = lws_context_user(ctx);
Web *w = static_cast<Web *>(user_ctx);
Api *a = w->getApi();
Http *s = static_cast<Http *>(user);
switch (reason) {
case LWS_CALLBACK_HTTP_BIND_PROTOCOL:
if (a == nullptr)
return -1;
new (s) Http(a, wsi);
a->sessions.push_back(s);
break;
case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
if (s == nullptr)
return -1;
a->sessions.remove(s);
s->~Http();
break;
case LWS_CALLBACK_HTTP_BODY:
s->read(in, len);
break;
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
ret = s->complete();
if (ret)
a->pending.push(s);
break;
case LWS_CALLBACK_HTTP_WRITEABLE:
ret = s->write();
if (ret) {
if (lws_http_transaction_completed(wsi))
return -1;
}
return 0;
default:
break;
}
return 0;
}