Skip to content

Commit

Permalink
#49 - Server can respond with data back to client.
Browse files Browse the repository at this point in the history
  • Loading branch information
PerMalmberg committed Jul 15, 2019
1 parent 9cdf0b4 commit 352105b
Show file tree
Hide file tree
Showing 21 changed files with 299 additions and 100 deletions.
3 changes: 2 additions & 1 deletion lib/smooth/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ set(SOURCES
application/network/http/regular/responses/StringResponse.cpp
application/network/http/regular/responses/HeaderOnlyResponse.cpp
application/network/http/regular/TemplateProcessor.cpp
application/network/http/websocket/responses/WSResponse.cpp
application/network/http/websocket/WebsocketProtocol.cpp
application/network/mqtt/packet/ConnAck.cpp
application/network/mqtt/packet/Connect.cpp
Expand Down Expand Up @@ -94,12 +95,12 @@ set(SOURCES
include/smooth/application/network/http/HTTPProtocol.h
include/smooth/application/network/http/URLEncoding.h
include/smooth/application/network/http/http_utils.h
include/smooth/application/network/http/IResponseOperation.h
include/smooth/application/network/http/regular/RegularHTTPProtocol.h
include/smooth/application/network/http/regular/TemplateProcessor.h
include/smooth/application/network/http/regular/ITemplateDataRetriever.h
include/smooth/application/network/http/regular/responses/ErrorResponse.h
include/smooth/application/network/http/regular/responses/FileContentResponse.h
include/smooth/application/network/http/regular/responses/IRequestResponeOperation.h
include/smooth/application/network/http/regular/responses/StringResponse.h
include/smooth/application/network/http/websocket/WebsocketProtocol.h
include/smooth/application/network/mqtt/event/BaseEvent.h
Expand Down
72 changes: 52 additions & 20 deletions lib/smooth/application/network/http/HTTPServerClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include <smooth/application/network/http/HTTPServerClient.h>
#include <smooth/application/network/http/IResponseOperation.h>
#include <smooth/application/network/http/websocket/responses/WSResponse.h>

namespace smooth::application::network::http
{
static const char* tag = "HTTPServerClient";
using namespace websocket;
using namespace websocket::responses;

void HTTPServerClient::event(
const core::network::event::DataAvailableEvent<HTTPProtocol>& event)
Expand All @@ -42,19 +46,19 @@ namespace smooth::application::network::http
std::vector<uint8_t> data;
auto res = current_operation->get_data(content_chunk_size, data);

if (res == responses::ResponseStatus::Error)
if (res == ResponseStatus::Error)
{
Log::error(tag, "Current operation reported error, closing server client.");
this->close();
}
else if (res == responses::ResponseStatus::EndOfData)
else if (res == ResponseStatus::EndOfData)
{
current_operation.reset();
// Immediately send next
send_first_part();
}
else if (res == responses::ResponseStatus::HasMoreData
|| res == responses::ResponseStatus::LastData)
else if (res == ResponseStatus::HasMoreData
|| res == ResponseStatus::LastData)
{
HTTPPacket p{data};
auto& tx = this->get_buffers()->get_tx_buffer();
Expand Down Expand Up @@ -140,7 +144,7 @@ namespace smooth::application::network::http


void HTTPServerClient::reply(
std::unique_ptr<responses::IRequestResponseOperation> response)
std::unique_ptr<IResponseOperation> response)
{
using namespace std::chrono;
const auto timeout = duration_cast<seconds>(this->socket->get_receive_timeout());
Expand All @@ -158,7 +162,7 @@ namespace smooth::application::network::http
}
}

void HTTPServerClient::reply_error(std::unique_ptr<responses::IRequestResponseOperation> response)
void HTTPServerClient::reply_error(std::unique_ptr<IResponseOperation> response)
{
operations.clear();
response->add_header(CONNECTION, "close");
Expand Down Expand Up @@ -189,19 +193,27 @@ namespace smooth::application::network::http
std::vector<uint8_t> data{};
auto res = current_operation->get_data(content_chunk_size, data);

if (res == responses::ResponseStatus::Error)
if (res == ResponseStatus::Error)
{
Log::error(tag, "Current operation reported error, closing server client.");
this->close();
}
else
{
// Whether or not everything is sent, send the current (possibly header-only) packet.
HTTPPacket p{current_operation->get_response_code(), "1.1", headers, data};
auto& tx = this->get_buffers()->get_tx_buffer();
tx.put(p);
if(mode == Mode::HTTP)
{
// Whether or not everything is sent, send the current (possibly header-only) packet.
HTTPPacket p{current_operation->get_response_code(), "1.1", headers, data};
tx.put(p);
}
else
{
HTTPPacket p{data};
tx.put(p);
}

if (res == responses::ResponseStatus::EndOfData)
if (res == ResponseStatus::EndOfData)
{
current_operation.reset();
// Immediately send next
Expand Down Expand Up @@ -306,7 +318,7 @@ namespace smooth::application::network::http
else
{
// Unsupported method.
reply(std::make_unique<responses::StringResponse>(ResponseCode::Method_Not_Allowed));
reply(std::make_unique<regular::responses::StringResponse>(ResponseCode::Method_Not_Allowed));
}
}
}
Expand All @@ -318,16 +330,36 @@ namespace smooth::application::network::http
typename HTTPProtocol::packet_type packet;
if (event.get(packet))
{
Log::info(tag, Format("Size: {1}", UInt64(packet.data().size())));
for(auto& c : packet.data())
auto ws_op = packet.ws_control_code();
if(ws_op >= WebsocketProtocol::OpCode::Close)
{
if(ws_op == WebsocketProtocol::OpCode::Close)
{
close();
}
else if(ws_op == WebsocketProtocol::OpCode::Ping)
{
// Reply with a ping
reply(std::make_unique<WSResponse>(WebsocketProtocol::OpCode::Pong));
}
}
else
{
std::cout << (char)c;
Log::info(tag, Format("Size: {1}", UInt64(packet.data().size())));
for (auto& c : packet.data())
{
std::cout << (char) c;
}
std::cout << std::endl;
bool first_packet = !packet.is_continuation();
bool last_packet = !packet.is_continued();
(void) first_packet;

if(last_packet)
{
reply(std::make_unique<WSResponse>("From server!!!!"));
}
}
std::cout << std::endl;
bool first_packet = !packet.is_continuation();
//bool last_packet = !packet.is_continued();
(void)first_packet;
}
}

}
6 changes: 4 additions & 2 deletions lib/smooth/application/network/http/regular/HTTPPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@
// 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 <smooth/application/network/http/regular/HTTPPacket.h>
#include <smooth/application/network/http/HTTPPacket.h>

#include <string>
#include <unordered_map>
#include <algorithm>
#include <smooth/application/network/http/http_utils.h>

namespace smooth::application::network::http::regular
namespace smooth::application::network::http
{
using namespace smooth::application::network::http::regular;

HTTPPacket::HTTPPacket(ResponseCode code, const std::string& version,
const std::unordered_map<std::string, std::string>& new_headers,
const std::vector<uint8_t>& response_content)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ namespace smooth::application::network::http::regular
{
}

std::unique_ptr<responses::IRequestResponseOperation>
std::unique_ptr<IResponseOperation>
TemplateProcessor::process_template(const smooth::core::filesystem::Path& path)
{
std::unique_ptr<responses::IRequestResponseOperation> res{};
std::unique_ptr<IResponseOperation> res{};

const auto& ext = path.extension();
bool is_template_file = template_files.find(ext) != template_files.end();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ namespace smooth::application::network::http::websocket
// Resize buffer to deliver exactly the number of received bytes to the application.
using vector_type = std::remove_reference<decltype(packet.data())>::type;
packet.data().resize(static_cast<vector_type::size_type>(received_payload_in_current_package));

set_message_properties(packet);
}
}

Expand All @@ -183,7 +185,6 @@ namespace smooth::application::network::http::websocket
return received_payload == payload_length
|| received_payload_in_current_package ==
static_cast<decltype(received_payload_in_current_package)>(content_chunk_size);

}

bool WebsocketProtocol::is_error()
Expand Down Expand Up @@ -214,4 +215,19 @@ namespace smooth::application::network::http::websocket
received_payload = 0;
received_payload_in_current_package = 0;
}

void WebsocketProtocol::set_message_properties(HTTPPacket& packet)
{
if(op_code == OpCode::Continuation)
{
packet.set_continuation();
}

if(!fin)
{
packet.set_continued();
}

packet.set_ws_control_code(op_code);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <smooth/application/network/http/websocket/responses/WSResponse.h>
#include <smooth/core/network/util.h>

namespace smooth::application::network::http::websocket::responses
{
ResponseStatus WSResponse::get_data(std::size_t max_amount, std::vector<uint8_t>& target)
{
auto res{ResponseStatus::EndOfData};

auto remaining = std::distance(data.begin(), data.end());
if (remaining > 0)
{
auto to_send = std::min(static_cast<std::size_t>(remaining), max_amount);
auto begin = data.begin();
auto end = data.begin() + static_cast<long>(to_send);

std::copy(std::make_move_iterator(begin), std::make_move_iterator(end),
std::back_inserter(target));
data.erase(begin, end);

// Anything still left?
remaining = std::distance(data.begin(), data.end());
res = remaining > 0 ? ResponseStatus::HasMoreData : ResponseStatus::LastData;
}

return res;
}

WSResponse::WSResponse(WebsocketProtocol::OpCode code)
{
// Set fin-bit
auto val = 0x80 | static_cast<uint8_t>(code);
data.emplace_back(val);
// No payload.
data.emplace_back(0);
}

WSResponse::WSResponse(const std::string& text)
{
auto val = 0x80 | static_cast<uint8_t>(WebsocketProtocol::OpCode::Text);
data.emplace_back(val);
if(text.size() <= 125)
{
data.emplace_back(text.size());
}
else if(text.size() > std::numeric_limits<uint16_t>::max())
{
std::array<uint8_t, 8> size{};
auto p = reinterpret_cast<uint64_t*>(&size[0]);
*p = static_cast<uint64_t>(text.size());
*p = smooth::core::network::hton(*p);
data.emplace_back(127);
std::copy(size.begin(), size.end(), std::back_inserter(data));
data.emplace_back(text.size());
}
else
{
std::array<uint8_t, 2> size{};
auto p = reinterpret_cast<uint16_t*>(&size[0]);
*p = static_cast<uint16_t>(text.size());
*p = smooth::core::network::hton(*p);
data.emplace_back(126);
std::copy(size.begin(), size.end(), std::back_inserter(data));
data.emplace_back(text.size());
}


std::copy(text.begin(), text.end(), std::back_inserter(data));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@
#include <vector>
#include <smooth/core/network/IPacketDisassembly.h>
#include <smooth/application/network/http/regular/ResponseCodes.h>
#include "HTTPMethod.h"
#include "regular/HTTPMethod.h"
#include "websocket/WebsocketProtocol.h"

namespace smooth::application::network::http::regular
namespace smooth::application::network::http
{
// This class acts as a carrier for two types of data:
// 1: Regular HTTP response/request data
// 2: Websocket data

class HTTPPacket
: public smooth::core::network::IPacketDisassembly
{
Expand All @@ -39,17 +44,16 @@ namespace smooth::application::network::http::regular

HTTPPacket(HTTPPacket&&) = default;

HTTPPacket(ResponseCode code, const std::string& version,
HTTPPacket(regular::ResponseCode code, const std::string& version,
const std::unordered_map<std::string, std::string>& new_headers,
const std::vector<uint8_t>& response_content);

HTTPPacket(HTTPMethod method, const std::string& url,
HTTPPacket(regular::HTTPMethod method, const std::string& url,
const std::unordered_map<std::string, std::string>& new_headers,
const std::vector<uint8_t>& response_content);

explicit HTTPPacket(std::vector<uint8_t>& response_content);


// Must return the total amount of bytes to send
int get_send_length() override
{
Expand Down Expand Up @@ -95,12 +99,12 @@ namespace smooth::application::network::http::regular
request_version = version;
}

void set_response_data(ResponseCode code)
void set_response_data(regular::ResponseCode code)
{
resp_code = code;
}

ResponseCode response_code() const
regular::ResponseCode response_code() const
{
return resp_code;
}
Expand Down Expand Up @@ -147,6 +151,16 @@ namespace smooth::application::network::http::regular
return end;
}

void set_ws_control_code(websocket::WebsocketProtocol::OpCode code)
{
ws_opcode = code;
}

websocket::WebsocketProtocol::OpCode ws_control_code() const
{
return ws_opcode;
}

static constexpr std::array<uint8_t, 4> ending{'\r', '\n', '\r', '\n'};

private:
Expand All @@ -159,8 +173,9 @@ namespace smooth::application::network::http::regular
std::string request_url{};
std::string request_version{};
std::vector<uint8_t> content{};
ResponseCode resp_code{};
regular::ResponseCode resp_code{};
bool continuation = false;
bool continued = false;
websocket::WebsocketProtocol::OpCode ws_opcode{websocket::WebsocketProtocol::OpCode::Continuation};
};
}
Loading

0 comments on commit 352105b

Please sign in to comment.