2021-03-21 17:40:36 -05:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
|
|
/*
|
2023-11-09 12:23:00 -06:00
|
|
|
* Copyright the Collabora Online contributors.
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
|
|
*
|
2021-03-21 17:40:36 -05:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <net/HttpRequest.hpp>
|
|
|
|
#include <net/Socket.hpp>
|
|
|
|
#include <common/Log.hpp>
|
|
|
|
#include <common/Util.hpp>
|
|
|
|
|
|
|
|
#include <chrono>
|
|
|
|
#include <string>
|
|
|
|
|
2024-02-20 11:23:58 -06:00
|
|
|
#ifndef APP_NAME
|
2021-03-21 17:40:36 -05:00
|
|
|
static_assert(false, "config.h must be included in the .cpp being compiled");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/// Handles incoming connections and dispatches to the appropriate handler.
|
|
|
|
class ServerRequestHandler final : public SimpleSocketHandler
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
/// Set the socket associated with this ResponseClient.
|
|
|
|
void onConnect(const std::shared_ptr<StreamSocket>& socket) override
|
|
|
|
{
|
2023-04-03 06:01:10 -05:00
|
|
|
LOG_ASSERT_MSG(socket, "Invalid socket passed to ServerRequestHandler::onConnect");
|
|
|
|
|
2021-03-21 17:40:36 -05:00
|
|
|
_socket = socket;
|
2023-04-03 06:01:10 -05:00
|
|
|
setLogContext(socket->getFD());
|
|
|
|
LOG_TRC("Connected to ServerRequestHandler");
|
2021-03-21 17:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Called after successful socket reads.
|
|
|
|
void handleIncomingMessage(SocketDisposition& disposition) override
|
|
|
|
{
|
|
|
|
std::shared_ptr<StreamSocket> socket = _socket.lock();
|
|
|
|
if (!socket)
|
|
|
|
{
|
|
|
|
LOG_ERR("Invalid socket while handling incoming client request");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-10-25 11:05:54 -05:00
|
|
|
Buffer& data = socket->getInBuffer();
|
2023-04-11 18:40:29 -05:00
|
|
|
if (data.empty())
|
|
|
|
{
|
|
|
|
LOG_DBG("No data to process from the socket");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_TRC("HandleIncomingMessage: buffer has:\n"
|
|
|
|
<< Util::dumpHex(std::string(data.data(), std::min(data.size(), 256UL))));
|
2021-03-21 17:40:36 -05:00
|
|
|
|
|
|
|
// Consume the incoming data by parsing and processing the body.
|
|
|
|
http::Request request;
|
|
|
|
const int64_t read = request.readData(data.data(), data.size());
|
2021-03-21 15:48:40 -05:00
|
|
|
if (read < 0)
|
2021-03-21 17:40:36 -05:00
|
|
|
{
|
2021-03-21 15:48:40 -05:00
|
|
|
// Interrupt the transfer.
|
|
|
|
disposition.setClosed();
|
|
|
|
socket->shutdown();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (read == 0)
|
|
|
|
{
|
|
|
|
// Not enough data.
|
|
|
|
return;
|
|
|
|
}
|
2021-03-21 17:40:36 -05:00
|
|
|
|
2021-03-21 15:48:40 -05:00
|
|
|
assert(read > 0 && "Must have read some data!");
|
2021-03-21 17:40:36 -05:00
|
|
|
|
2021-03-21 15:48:40 -05:00
|
|
|
// Remove consumed data.
|
2021-10-25 11:05:54 -05:00
|
|
|
data.eraseFirst(read);
|
2021-03-21 15:48:40 -05:00
|
|
|
|
2023-04-11 18:40:29 -05:00
|
|
|
const int fd = socket->getFD();
|
|
|
|
LOG_TRC("HandleIncomingMessage: removed " << read << " bytes to have " << data.size()
|
|
|
|
<< " in the buffer");
|
2021-03-21 15:48:40 -05:00
|
|
|
|
|
|
|
if (request.getVerb() == http::Request::VERB_GET)
|
|
|
|
{
|
|
|
|
// Return test data.
|
|
|
|
if (Util::startsWith(request.getUrl(), "/status/"))
|
2021-03-21 17:40:36 -05:00
|
|
|
{
|
2021-03-21 15:48:40 -05:00
|
|
|
const auto statusCode
|
|
|
|
= Util::i32FromString(request.getUrl().substr(sizeof("/status")));
|
|
|
|
const auto reason = http::getReasonPhraseForCode(statusCode.first);
|
2023-04-11 18:40:29 -05:00
|
|
|
LOG_TRC("HandleIncomingMessage: got StatusCode " << statusCode.first
|
|
|
|
<< ", sending back: " << reason);
|
2021-03-21 15:48:40 -05:00
|
|
|
|
2023-03-01 21:05:02 -06:00
|
|
|
http::Response response(http::StatusLine(statusCode.first), fd);
|
2021-03-21 15:48:40 -05:00
|
|
|
if (statusCode.first == 402)
|
|
|
|
{
|
2021-03-20 16:24:17 -05:00
|
|
|
response.setBody("Pay me!");
|
2021-03-21 15:48:40 -05:00
|
|
|
}
|
|
|
|
else if (statusCode.first == 406)
|
2021-03-21 17:40:36 -05:00
|
|
|
{
|
2021-03-20 16:24:17 -05:00
|
|
|
response.setBody(
|
|
|
|
R"({"message": "Client did not request a supported media type.", )"
|
|
|
|
R"("accept": ["image/webp", "image/svg+xml", "image/jpeg", "image/png", "image/*"]})");
|
2021-03-21 17:40:36 -05:00
|
|
|
}
|
2021-03-21 15:48:40 -05:00
|
|
|
else if (statusCode.first == 418)
|
2021-03-21 17:40:36 -05:00
|
|
|
{
|
2021-03-20 16:24:17 -05:00
|
|
|
response.setBody("I'm a teapot!");
|
2021-03-21 17:40:36 -05:00
|
|
|
}
|
2021-03-21 15:48:40 -05:00
|
|
|
|
2021-03-20 16:24:17 -05:00
|
|
|
if (response.getBody().empty() && statusCode.first >= 200 && statusCode.first != 204
|
|
|
|
&& statusCode.first != 304) // No Content for other tags.
|
2021-03-21 15:48:40 -05:00
|
|
|
response.set("Content-Length", "0");
|
|
|
|
|
2021-03-20 16:24:17 -05:00
|
|
|
socket->send(response);
|
2021-03-21 17:40:36 -05:00
|
|
|
}
|
2021-04-12 20:52:03 -05:00
|
|
|
else if (request.getUrl() == "/timeout")
|
|
|
|
{
|
|
|
|
// Don't send anything back.
|
|
|
|
}
|
2021-05-31 21:34:47 -05:00
|
|
|
else if (Util::startsWith(request.getUrl(), "/inject"))
|
|
|
|
{
|
|
|
|
// /inject/<hex data> sends back the data (in binary form)
|
2022-08-22 15:00:05 -05:00
|
|
|
// verbatim. It doesn't add headers or anything at all.
|
2021-05-31 21:34:47 -05:00
|
|
|
const std::string hex = request.getUrl().substr(sizeof("/inject"));
|
|
|
|
const std::string bytes = Util::hexStringToBytes(
|
|
|
|
reinterpret_cast<const uint8_t*>(hex.data()), hex.size());
|
|
|
|
socket->send(bytes);
|
2022-08-06 10:23:11 -05:00
|
|
|
socket->shutdown();
|
2021-05-31 21:34:47 -05:00
|
|
|
}
|
2021-03-21 17:40:36 -05:00
|
|
|
else
|
|
|
|
{
|
2023-03-01 21:05:02 -06:00
|
|
|
http::Response response(http::StatusCode::OK, fd);
|
2021-03-20 16:24:17 -05:00
|
|
|
if (Util::startsWith(request.getUrl(), "/echo/"))
|
2021-09-10 08:04:56 -05:00
|
|
|
{
|
|
|
|
if (Util::startsWith(request.getUrl(), "/echo/chunked/"))
|
|
|
|
{
|
|
|
|
response.set("transfer-encoding", "chunked");
|
|
|
|
std::string body = request.getUrl().substr(sizeof("/echo/chunked"));
|
|
|
|
while (!body.empty())
|
|
|
|
{
|
|
|
|
if (body.size() < 5)
|
|
|
|
{
|
|
|
|
response.appendChunk(body);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto half = body.size() / 2;
|
|
|
|
const auto size = (Util::rng::getNext() % half) + 1; // 0-size means the end.
|
|
|
|
|
|
|
|
const auto chunk = body.substr(0, size);
|
|
|
|
response.appendChunk(chunk);
|
|
|
|
body = body.substr(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
response.appendChunk(std::string()); // Empty chunk to end.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
response.setBody(request.getUrl().substr(sizeof("/echo")));
|
|
|
|
}
|
2021-04-12 20:52:03 -05:00
|
|
|
else
|
|
|
|
response.setBody("You have reached HttpTestServer " + request.getUrl());
|
2021-03-20 16:24:17 -05:00
|
|
|
socket->send(response);
|
2021-03-21 17:40:36 -05:00
|
|
|
}
|
|
|
|
}
|
2021-03-21 15:48:40 -05:00
|
|
|
else
|
2021-03-21 17:40:36 -05:00
|
|
|
{
|
2023-03-01 21:05:02 -06:00
|
|
|
http::Response response(http::StatusCode::NotImplemented, fd);
|
2021-03-21 15:48:40 -05:00
|
|
|
response.set("Content-Length", "0");
|
2021-03-20 16:24:17 -05:00
|
|
|
socket->send(response);
|
2021-03-21 17:40:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int getPollEvents(std::chrono::steady_clock::time_point /* now */,
|
|
|
|
int64_t& /* timeoutMaxMs */) override
|
|
|
|
{
|
|
|
|
return POLLIN;
|
|
|
|
}
|
|
|
|
|
2021-03-07 11:57:13 -06:00
|
|
|
void performWrites(std::size_t) override
|
2021-03-21 17:40:36 -05:00
|
|
|
{
|
|
|
|
std::shared_ptr<StreamSocket> socket = _socket.lock();
|
|
|
|
if (!socket)
|
|
|
|
{
|
|
|
|
LOG_ERR("Invalid socket while performing writes.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer& out = socket->getOutBuffer();
|
|
|
|
LOG_TRC("performWrites: " << out.size() << " bytes.");
|
2021-03-21 08:51:37 -05:00
|
|
|
socket->flush();
|
2021-03-21 17:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// The socket that owns us (we can't own it).
|
|
|
|
std::weak_ptr<StreamSocket> _socket;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|